web-dev-qa-db-ja.com

C#でより大きな文字列で部分文字列のすべての位置を見つける

解析する必要がある大きな文字列があり、extract"(me,i-have lots. of]punctuationのすべてのインスタンスを見つけて、それぞれのインデックスをリストに保存する必要があります。

したがって、この文字列の部分が大きい文字列の先頭と中央にあり、両方が見つかって、インデックスがListに追加されるとしましょう。 Listには0とその他のインデックスが含まれます。

私は遊んでいて、string.IndexOfほぼ私が探しているもの、そしていくつかのコードを書いた-しかしそれは機能せず、私は理解できなかった何が間違っているのかを正確に確認してください:

List<int> inst = new List<int>();
int index = 0;
while (index < source.LastIndexOf("extract\"(me,i-have lots. of]punctuation", 0) + 39)
{
    int src = source.IndexOf("extract\"(me,i-have lots. of]punctuation", index);
    inst.Add(src);
    index = src + 40;
}
  • inst =リスト
  • source =大きな文字列

より良いアイデアはありますか?

65
caesay

以下に拡張メソッドの例を示します。

public static List<int> AllIndexesOf(this string str, string value) {
    if (String.IsNullOrEmpty(value))
        throw new ArgumentException("the string to find may not be empty", "value");
    List<int> indexes = new List<int>();
    for (int index = 0;; index += value.Length) {
        index = str.IndexOf(value, index);
        if (index == -1)
            return indexes;
        indexes.Add(index);
    }
}

これを静的クラスに入れ、usingで名前空間をインポートすると、任意の文字列のメソッドとして表示され、次のようにできます。

List<int> indexes = "fooStringfooBar".AllIndexesOf("foo");

拡張メソッドの詳細については、 http://msdn.Microsoft.com/en-us/library/bb383977.aspx

反復子を使用した場合も同じです:

public static IEnumerable<int> AllIndexesOf(this string str, string value) {
    if (String.IsNullOrEmpty(value))
        throw new ArgumentException("the string to find may not be empty", "value");
    for (int index = 0;; index += value.Length) {
        index = str.IndexOf(value, index);
        if (index == -1)
            break;
        yield return index;
    }
}
119
Matti Virkkunen

組み込みのRegExクラスを使用しないのはなぜですか。

public static IEnumerable<int> GetAllIndexes(this string source, string matchString)
{
   matchString = Regex.Escape(matchString);
   foreach (Match match in Regex.Matches(source, matchString))
   {
      yield return match.Index;
   }
}

式を再利用する必要がある場合は、式をコンパイルしてどこかにキャッシュします。 matchStringパラメーターを、再利用の場合の別のオーバーロードの正規表現matchExpressionに変更します。

15
csaam

lINQを使用する

public static IEnumerable<int> IndexOfAll(this string sourceString, string subString)
{
    return Regex.Matches(sourceString, subString).Cast<Match>().Select(m => m.Index);
}
8
ehosca

洗練されたバージョン+サポートを無視するケース:

public static int[] AllIndexesOf(string str, string substr, bool ignoreCase = false)
{
    if (string.IsNullOrWhiteSpace(str) ||
        string.IsNullOrWhiteSpace(substr))
    {
        throw new ArgumentException("String or substring is not specified.");
    }

    var indexes = new List<int>();
    int index = 0;

    while ((index = str.IndexOf(substr, index, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) != -1)
    {
        indexes.Add(index++);
    }

    return indexes.ToArray();
}
5
net_prog

正規表現なしで、文字列比較タイプを使用:

string search = "123aa456AA789bb9991AACAA";
string pattern = "AA";
Enumerable.Range(0, search.Length)
   .Select(index => { return new { Index = index, Length = (index + pattern.Length) > search.Length ? search.Length - index : pattern.Length }; })
   .Where(searchbit => searchbit.Length == pattern.Length && pattern.Equals(search.Substring(searchbit.Index, searchbit.Length),StringComparison.OrdinalIgnoreCase))
   .Select(searchbit => searchbit.Index)

これは{3,8,19,22}を返します。空のパターンはすべての位置に一致します。

複数のパターンの場合:

string search = "123aa456AA789bb9991AACAA";
string[] patterns = new string[] { "aa", "99" };
patterns.SelectMany(pattern => Enumerable.Range(0, search.Length)
   .Select(index => { return new { Index = index, Length = (index + pattern.Length) > search.Length ? search.Length - index : pattern.Length }; })
   .Where(searchbit => searchbit.Length == pattern.Length && pattern.Equals(search.Substring(searchbit.Index, searchbit.Length), StringComparison.OrdinalIgnoreCase))
   .Select(searchbit => searchbit.Index))

これは{3、8、19、22、15、16}を返します

1
Sean

少なくとも2つの提案されたソリューションが重複する検索ヒットを処理しないことに気付きました。緑色のチェックマークが付いているものはチェックしませんでした。重複する検索ヒットを処理するものは次のとおりです。

    public static List<int> GetPositions(this string source, string searchString)
    {
        List<int> ret = new List<int>();
        int len = searchString.Length;
        int start = -1;
        while (true)
        {
            start = source.IndexOf(searchString, start +1);
            if (start == -1)
            {
                break;
            }
            else
            {
                ret.Add(start);
            }
        }
        return ret;
    }
1
Kevin Baker

こんにちは@Matti Virkkunenによる素敵な回答

public static List<int> AllIndexesOf(this string str, string value) {
    if (String.IsNullOrEmpty(value))
        throw new ArgumentException("the string to find may not be empty", "value");
    List<int> indexes = new List<int>();
    for (int index = 0;; index += value.Length) {
        index = str.IndexOf(value, index);
        if (index == -1)
            return indexes;
        indexes.Add(index);
        index--;
    }
}

ただし、これはAOOAOOAのようなテストケースを対象としています。

aOOAとAOOA

出力0および3

1
Pranay Deep
public List<int> GetPositions(string source, string searchString)
{
    List<int> ret = new List<int>();
    int len = searchString.Length;
    int start = -len;
    while (true)
    {
        start = source.IndexOf(searchString, start + len);
        if (start == -1)
        {
            break;
        }
        else
        {
            ret.Add(start);
        }
    }
    return ret;
}

次のように呼び出します。

List<int> list = GetPositions("bob is a chowder head bob bob sldfjl", "bob");
// list will contain 0, 22, 26
1
MusiGenesis
public static Dictionary<string, IEnumerable<int>> GetWordsPositions(this string input, string[] Susbtrings)
{
    Dictionary<string, IEnumerable<int>> WordsPositions = new Dictionary<string, IEnumerable<int>>();
    IEnumerable<int> IndexOfAll = null;
    foreach (string st in Susbtrings)
    {
        IndexOfAll = Regex.Matches(input, st).Cast<Match>().Select(m => m.Index);
        WordsPositions.Add(st, IndexOfAll);

    }
    return WordsPositions;
}

@ csam は理論上正しいですが、彼のコードはコンパイルされず、リファクタリングできます

public static IEnumerable<int> IndexOfAll(this string sourceString, string matchString)
{
    matchString = Regex.Escape(matchString);
    return from Match match in Regex.Matches(sourceString, matchString) select match.Index;
}
0
arame3333

大きな文字列内の文字列の複数のインスタンスを見つけるために使用したコードに基づいて、コードは次のようになります。

List<int> inst = new List<int>();
int index = 0;
while (index >=0)
{
    index = source.IndexOf("extract\"(me,i-have lots. of]punctuation", index);
    inst.Add(index);
    index++;
}
0
Corin