在C#中查找更大字符串中子字符串的所有位置

cae*_*say 69 .net c# asp.net string

我有一个需要解析的大字符串,我需要查找所有实例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;
}
Run Code Online (Sandbox Code Playgroud)
  • inst =清单
  • source =大字符串

有更好的想法吗?

Mat*_*nen 123

这是一个示例扩展方法:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果将其放入静态类并导入命名空间using,则它在任何字符串上显示为方法,您可以这样做:

List<int> indexes = "fooStringfooBar".AllIndexesOf("foo");
Run Code Online (Sandbox Code Playgroud)

有关扩展方法的更多信息,请访问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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么不使用IEnumerable <int>并生成返回索引而不是索引列表? (6认同)
  • 注意!由于添加了`value.Length`,您可能会错过嵌套匹配!示例:"这是一个NestedNestedNested匹配测试!" 匹配"NestedNested"将只找到一个索引,但不会找到嵌套索引.要解决这个问题,只需在循环中添加`+ = 1`而不是`+ = value.Length`. (6认同)
  • @m0sa:好点。添加了另一个版本只是为了好玩。 (2认同)
  • @ PedroC88:使用`yield`将使代码"懒惰".它不会将所有索引收集到方法中的内存列表中.对性能有什么样的实际效果取决于很多因素. (2认同)
  • 您的解决方案不正确!为了在“aaa”中查找“aa”,输出应该是 [0, 1] 但你的算法返回 [0] (2认同)

csa*_*aam 15

为什么不使用内置的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;
   }
}
Run Code Online (Sandbox Code Playgroud)

如果你确实需要重用表达式,那么编译它并在某处缓存它.将matchString参数更改为另一个重载中的Regex matchExpression以用于重用案例.

  • 请注意,此方法与已接受的答案存在相同的缺陷。如果您的源字符串是“ccc”并且模式是“cc”,那么它将只返回一次。 (2认同)
  • @user280498 这不一定是一个“缺陷”,具体取决于使用场景...... (2认同)

eho*_*sca 8

使用LINQ

public static IEnumerable<int> IndexOfAll(this string sourceString, string subString)
{
    return Regex.Matches(sourceString, subString).Cast<Match>().Select(m => m.Index);
}
Run Code Online (Sandbox Code Playgroud)

  • 不过你忘记转义 subString 了。 (2认同)
  • 对于“aaa”和“aa”输入,似乎不起作用。 (2认同)

M.K*_*ani 6

可以使用 O(N + M) 中的KMP算法以有效的时间复杂度完成,其中 N 是 的长度text,M 是 的长度pattern

这是实现和使用:

static class StringExtensions
{
    public static IEnumerable<int> AllIndicesOf(this string text, string pattern)
    {
        if (string.IsNullOrEmpty(pattern))
        {
            throw new ArgumentNullException(nameof(pattern));
        }
        return Kmp(text, pattern);
    }

    private static IEnumerable<int> Kmp(string text, string pattern)
    {
        int M = pattern.Length;
        int N = text.Length;

        int[] lps = LongestPrefixSuffix(pattern);
        int i = 0, j = 0; 

        while (i < N)
        {
            if (pattern[j] == text[i])
            {
                j++;
                i++;
            }
            if (j == M)
            {
                yield return i - j;
                j = lps[j - 1];
            }

            else if (i < N && pattern[j] != text[i])
            {
                if (j != 0)
                {
                    j = lps[j - 1];
                }
                else
                {
                    i++;
                }
            }
        }
    }

    private static int[] LongestPrefixSuffix(string pattern)
    {
        int[] lps = new int[pattern.Length];
        int length = 0;
        int i = 1;

        while (i < pattern.Length)
        {
            if (pattern[i] == pattern[length])
            {
                length++;
                lps[i] = length;
                i++;
            }
            else
            {
                if (length != 0)
                {
                    length = lps[length - 1];
                }
                else
                {
                    lps[i] = length;
                    i++;
                }
            }
        }
        return lps;
    }
Run Code Online (Sandbox Code Playgroud)

这是如何使用它的示例:

static void Main(string[] args)
    {
        string text = "this is a test";
        string pattern = "is";
        foreach (var index in text.AllIndicesOf(pattern))
        {
            Console.WriteLine(index); // 2 5
        }
    }
Run Code Online (Sandbox Code Playgroud)


net*_*rog 5

抛光版+案例忽略支持:

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();
}
Run Code Online (Sandbox Code Playgroud)