独自のループコードを記述せずに、C#で文字列内の最初の非空白文字のインデックス(または、より一般的には、条件に一致する最初の文字のインデックス)を取得する手段はありますか?
編集
「独自のループコードを作成する」とは、作業中のロジックを乱雑にすることなく問題を解決するコンパクトな式を探していることを意味します。
その点でご迷惑をおかけしましたことをお詫び申し上げます。
独自のループコードを記述せずに、C#で文字列内の最初の非空白文字のインデックス(または、より一般的には、条件に一致する最初の文字のインデックス)を取得する手段はありますか?
編集
「独自のループコードを作成する」とは、作業中のロジックを乱雑にすることなく問題を解決するコンパクトな式を探していることを意味します。
その点でご迷惑をおかけしましたことをお詫び申し上げます。
Astring
はもちろん anIEnumerable<char>
なので、Linq を使用できます。
int offset = someString.TakeWhile(c => char.IsWhiteSpace(c)).Count();
シーケンス内のカスタム述語を満たす最初の要素のインデックスを返す独自の拡張メソッドを定義するのが好きです。
/// <summary>
/// Returns the index of the first element in the sequence
/// that satisfies a condition.
/// </summary>
/// <typeparam name="TSource">
/// The type of the elements of <paramref name="source"/>.
/// </typeparam>
/// <param name="source">
/// An <see cref="IEnumerable{T}"/> that contains
/// the elements to apply the predicate to.
/// </param>
/// <param name="predicate">
/// A function to test each element for a condition.
/// </param>
/// <returns>
/// The zero-based index position of the first element of <paramref name="source"/>
/// for which <paramref name="predicate"/> returns <see langword="true"/>;
/// or -1 if <paramref name="source"/> is empty
/// or no element satisfies the condition.
/// </returns>
public static int IndexOf<TSource>(this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
int i = 0;
foreach (TSource element in source)
{
if (predicate(element))
return i;
i++;
}
return -1;
}
その後、LINQ を使用して元の問題に対処できます。
string str = " Hello World";
int i = str.IndexOf<char>(c => !char.IsWhiteSpace(c));
string s= " \t Test";
Array.FindIndex(s.ToCharArray(), x => !char.IsWhiteSpace(x));
6 を返します
条件を追加するには...
Array.FindIndex(s.ToCharArray(), x => !char.IsWhiteSpace(x) && your condition);
ここにはいくつかのソリューションがあったので、それぞれのパフォーマンスを確認するためにいくつかのパフォーマンス テストを行うことにしました。興味のある人のためにこれらの結果を共有することにしました...
int iterations = 1000000;
int result = 0;
string s= " \t Test";
System.Diagnostics.Stopwatch watch = new Stopwatch();
// Convert to char array and use FindIndex
watch.Start();
for (int i = 0; i < iterations; i++)
result = Array.FindIndex(s.ToCharArray(), x => !char.IsWhiteSpace(x));
watch.Stop();
Console.WriteLine("Convert to char array and use FindIndex: " + watch.ElapsedMilliseconds);
// Trim spaces and get index of first character
watch.Restart();
for (int i = 0; i < iterations; i++)
result = s.IndexOf(s.TrimStart().Substring(0,1));
watch.Stop();
Console.WriteLine("Trim spaces and get index of first character: " + watch.ElapsedMilliseconds);
// Use extension method
watch.Restart();
for (int i = 0; i < iterations; i++)
result = s.IndexOf<char>(c => !char.IsWhiteSpace(c));
watch.Stop();
Console.WriteLine("Use extension method: " + watch.ElapsedMilliseconds);
// Loop
watch.Restart();
for (int i = 0; i < iterations; i++)
{
result = 0;
foreach (char c in s)
{
if (!char.IsWhiteSpace(c))
break;
result++;
}
}
watch.Stop();
Console.WriteLine("Loop: " + watch.ElapsedMilliseconds);
結果はミリ秒単位です....
Where s = " \t Test"
char 配列に変換し、FindIndex を使用:154
スペースを削除し、最初の文字のインデックスを取得:189
拡張メソッドを使用:234
ループ:146
s = "Test"
char 配列に変換し、FindIndex を使用:39
スペースを削除し、最初の文字のインデックスを取得:155
拡張メソッドを使用:57
ループ:15
s = (スペースなしの 1000 文字の文字列)
char 配列に変換し、FindIndex を使用します:506
スペースを削除し、最初の文字のインデックスを取得します:534
拡張メソッドを使用します:51
ループ:15
s = (" \t Test" で始まる 1000 文字の文字列)
char 配列に変換し、FindIndex を使用:609
スペースを削除し、最初の文字のインデックスを取得:1103
拡張メソッドを使用:226
ループ:146
独自の結論を引き出しますが、私の結論は、実際のシーンではパフォーマンスの違いは重要ではないため、最も好きな方を使用することです。
String.IndexOfAny関数を使用すると、指定した Unicode 文字の配列内で最初に出現する任意の文字を返すことができます。
または、文字列の先頭からすべての空白文字を削除するString.TrimStart関数を使用できます。最初の非空白文字のインデックスは、元の文字列とトリミングされた文字列の長さの差です。
トリミングする文字のセットを選択することもできます:)
基本的に、限られた文字セット (数字としましょう) を探している場合は、最初の方法を使用する必要があります。
限られた文字セット (空白など) を無視しようとしている場合は、2 番目の方法を使用する必要があります。
最後の方法は、Linqメソッドを使用することです。
string s = " qsdmlkqmlsdkm";
Console.WriteLine(s.TrimStart());
Console.WriteLine(s.Length - s.TrimStart().Length);
Console.WriteLine(s.FirstOrDefault(c => !Char.IsWhiteSpace(c)));
Console.WriteLine(s.IndexOf(s.FirstOrDefault(c => !Char.IsWhiteSpace(c))));
出力:
qsdmlkqmlsdkm
8
q
8
var match = Regex.Match(" \t test ", @"\S"); // \S means all characters that are not whitespace
if (match.Success)
{
int index = match.Index;
//do something with index
}
else
{
//there were no non-whitespace characters, handle appropriately
}
これを頻繁に行う場合は、パフォーマンス上の理由からRegex
、このパターン用にコンパイルされたものをキャッシュする必要があります。
static readonly Regex nonWhitespace = new Regex(@"\S");
次に、次のように使用します。
nonWhitespace.Match(" \t test ");
トリミングして最初の文字を取得し、IndexOf を使用できます。
非常に簡単な解決策があります
string test = " hello world";
int pos = test.ToList<char>().FindIndex(x => char.IsWhiteSpace(x) == false);
pos は 4 になります
次のようなより複雑な条件を設定できます。
pos = test.ToList<char>().FindIndex((x) =>
{
if (x == 's') //Your complex conditions go here
return true;
else
return false;
}
);
はい、これを試すことができます:
string stg = " xyz";
int indx = (stg.Length - stg.Trim().Length);
どこかで何かがループしているでしょう。空白とそうでないものを完全に制御するには、linq to objects を使用してループを実行できます。
int index = Array.FindIndex(
s.ToCharArray(),
x => !(new [] { '\t', '\r', '\n', ' '}.Any(c => c == x)));