【问题标题】:How would you count occurrences of a string (actually a char) within a string?您如何计算字符串中字符串(实际上是字符)的出现次数?
【发布时间】:2010-10-07 05:12:53
【问题描述】:

我正在做一些事情,我意识到我想计算我可以在一个字符串中找到多少个/s,然后我突然想到,有几种方法可以做到这一点,但无法决定什么是最好的(或最简单的)是。

目前我正在使用类似的东西:

string source = "/once/upon/a/time/";
int count = source.Length - source.Replace("/", "").Length;

但我一点也不喜欢,有没有接受者?

我真的不想为此挖出RegEx,对吗?

我知道我的字符串中会有我要搜索的词,所以你可以假设...

当然对于字符串 where length > 1,

string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = ( haystack.Length - haystack.Replace(needle,"").Length ) / needle.Length;

【问题讨论】:

  • +1:我必须说这是一种非常不同的计数方式。我对基准测试结果感到惊讶:)
  • 并没有太大的不同……这是在 SQL 中实现此功能的典型方式:LEN(ColumnToCheck) - LEN(REPLACE(ColumnToCheck,"N",""))
  • 事实上你应该除以"/".Length
  • 请问,您的要求是“/////”中出现“//”的次数应该是多少? 2个还是4个?
  • 使用正则表达式可能是最好的方法

标签: c# string


【解决方案1】:

如果您使用的是 .NET 3.5,您可以使用 LINQ 在单行中执行此操作:

int count = source.Count(f => f == '/');

如果你不想使用 LINQ,你可以这样做:

int count = source.Split('/').Length - 1;

您可能会惊讶地发现,您的原始技术似乎比其中任何一种都快 30%!我刚刚用“/once/once/a/time/”做了一个快速基准测试,结果如下:

您的原件 = 12 秒
source.Count = 19s
source.Split = 17s
foreach (from bobwienholt's answer) = 10s

(时间是 50,000,000 次迭代,因此您不太可能注意到现实世界中有太大差异。)

【讨论】:

  • 是的,VS 在字符串类上隐藏了 LINQ 扩展方法。我猜他们认为开发人员不希望所有这些扩展方法都出现在字符串类上。可能是一个明智的决定。
  • 这种行为可能是因为 VS2010 自动在新的类文件中包含 System.Linq,而 VS2008 可能没有。命名空间需要在 intellisense 中工作。
  • 请注意,计数和拆分解决方案仅在您计算字符数时才有效。它们不会像 OP 的解决方案那样使用字符串。
  • f == '\' 是关于字符串中的字符,而不是字符串中的字符串
  • 这似乎是对另一个问题的回答:“如何计算字符串中字符的出现次数?”
【解决方案2】:
string source = "/once/upon/a/time/";
int count = 0;
foreach (char c in source) 
  if (c == '/') count++;

必须比 source.Replace() 本身更快。

【讨论】:

  • 你可以通过改用 for 而不是 foreach 获得一点点改进,但​​只有一点点。
  • 没有。该问题要求计算字符串的出现次数,而不是字符。
  • 这是对字符串中的字符进行计数。标题是关于计算字符串中的字符串
  • @Mark 刚刚用 for 循环对其进行了测试,它实际上比使用 foreach 慢。可能是因为边界检查? (时间为 1.65 秒,而 5 百万次迭代为 2.05 秒。)
  • 虽然问题是要求字符串中的一个字符串,但 OP 发布的示例问题实际上只是一个字符,在这种情况下,我认为这个答案仍然是一个有效的解决方案,因为它显示了一种更好的方法(字符搜索而不是字符串搜索)来解决手头的问题。
【解决方案3】:
int count = new Regex(Regex.Escape(needle)).Matches(haystack).Count;

【讨论】:

  • +1 - 在某些情况下,您可能需要添加 RegexOptions.IgnoreCase
  • 这不是非常低吗?
  • Regex 开销并不理想,加上“我真的不想为此挖掘 RegEx,是吗?”
  • 可能不想Regex.Escape(...)所以new System.Text.RegularExpressions.Regex(needle).Matches(haystack).Count;
  • 我选择了这个,因为它可以搜索字符串,而不仅仅是字符。
【解决方案4】:

如果您希望能够搜索整个字符串,而不仅仅是字符:

src.Select((c, i) => src.Substring(i))
    .Count(sub => sub.StartsWith(target))

读作“对于字符串中的每个字符,取从该字符开始的其余字符串作为子字符串;如果它以目标字符串开头,则计算它。”

【讨论】:

  • 不知道如何以比给出的描述更清楚的方式解释它。什么令人困惑?
  • 超级慢! 在一个html页面上尝试了大约2分钟,而这个页面上的其他方法需要2秒。答案是正确的;它太慢了,无法使用。
  • 同意,太慢了。我是 linq 风格的解决方案的忠实粉丝,但这个是不可行的。
  • 请注意,之所以这么慢,是因为它创建了 n 个字符串,因此分配了大约 n^2/2 个字节。
  • OutOfMemoryException 针对我的 210000 个字符的字符串抛出。
【解决方案5】:

我进行了一些研究,发现Richard Watson's 解决方案在大多数情况下最快。这是帖子中每个解决方案的结果表(除了那些使用 Regex 因为它在解析像“test {test”这样的字符串时抛出异常)

    Name      | Short/char |  Long/char | Short/short| Long/short |  Long/long |
    Inspite   |         134|        1853|          95|        1146|         671|
    LukeH_1   |         346|        4490|         N/A|         N/A|         N/A|
    LukeH_2   |         152|        1569|         197|        2425|        2171|
Bobwienholt   |         230|        3269|         N/A|         N/A|         N/A|
Richard Watson|          33|         298|         146|         737|         543|
StefanosKargas|         N/A|         N/A|         681|       11884|       12486|

您可以看到,如果在短字符串(10-50 个字符)中查找短子字符串(1-5 个字符)的出现次数,则首选原始算法。

另外,对于多字符子字符串,您应该使用以下代码(基于Richard Watson's 解决方案)

int count = 0, n = 0;

if(substring != "")
{
    while ((n = source.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
    {
        n += substring.Length;
        ++count;
    }
}

【讨论】:

  • 我正要添加自己的“低级”解决方案(不创建子字符串、使用替换/拆分或任何正则表达式/Linq),但你的可能比我的更好(并且至少更短)。谢谢!
  • 对于正则表达式解决方案,添加Regex.Escape(needle)
  • 为别人指出,搜索值是否为空需要检查,否则会陷入死循环。
  • 也许只有我一个人,但对于 source="aaa" substring="aa",我希望得到 2,而不是 1。要“解决”这个问题,请将 n += substring.Length 更改为 n++
  • 您可以添加overlapped 标志以满足您的情况,如下所示:overlapped=True;.... if(overlapped) {++n;} else {n += substring.Length;}
【解决方案6】:

LINQ 适用于所有集合,并且由于字符串只是字符的集合,那么这个漂亮的小单行线怎么样:

var count = source.Count(c => c == '/');

确保您的代码文件顶部有 using System.Linq;,因为 .Count 是该命名空间的扩展方法。

【讨论】:

  • 真的值得在那里使用 var 吗?是否有可能将 Count 替换为不返回 int 的内容?
  • @Whatsit:您可以只用左手输入“var”,而“int”则需要双手;)
  • int 字母都存在于 home 键中,而 var 不存在。呃..等等,我正在使用 Dvorak
  • @BDotA 确保你有一个'using System.Linq;'在文件的顶部。此外,智能感知可能会对您隐藏 .Count 调用,因为它是一个字符串。即便如此,它也能正常编译和运行。
  • @JudahGabrielHimango 我认为应该使用 var 特别是当变量类型很明显时(并且为了简洁和一致性)
【解决方案7】:
string source = "/once/upon/a/time/";
int count = 0;
int n = 0;

while ((n = source.IndexOf('/', n)) != -1)
{
   n++;
   count++;
}

在我的计算机上,对于 5000 万次迭代,它比针对每个字符的解决方案快大约 2 秒。

2013 年修订:

将字符串更改为 char[] 并对其进行迭代。将 50m 迭代的总时间再缩短一两秒!

char[] testchars = source.ToCharArray();
foreach (char c in testchars)
{
     if (c == '/')
         count++;
}

这还是更快:

char[] testchars = source.ToCharArray();
int length = testchars.Length;
for (int n = 0; n < length; n++)
{
    if (testchars[n] == '/')
        count++;
}

为了更好地衡量,从数组末尾迭代到 0 似乎是最快的,大约 5%。

int length = testchars.Length;
for (int n = length-1; n >= 0; n--)
{
    if (testchars[n] == '/')
        count++;
}

我想知道为什么这可能是并且正在谷歌搜索(我记得一些关于反向迭代更快的事情),并且遇到了这个 SO 问题,它已经很烦人地使用了 string to char[] 技术。不过,我认为反转技巧在这种情况下是新的。

What is the fastest way to iterate through individual characters in a string in C#?

【讨论】:

  • 您可以输入source.IndexOf('/', n + 1) 并丢失n++ 和括号中的while :) 另外,输入变量string word = "/" 而不是字符。
  • 嗨 Niko,查看新答案。不过,制作可变长度子字符串可能更难。
  • 我通过遍历子字符串使用了类似的东西;直到我意识到 indexOf 有一个 startIndex。我最喜欢第一个解决方案,因为它在速度和内存占用之间取得了很好的平衡。
  • 我在某处读到,向后迭代更快,因为将值与 0 进行比较会更快
  • @shitpoet 是的。如果您查看底层代码,它是一个本机调用。 public char[] toCharArray() {... System.arraycopy(value, 0, result, 0, value.length); ... }
【解决方案8】:

这些都只适用于单字符搜索词...

countOccurences("the", "the answer is the answer");

int countOccurences(string needle, string haystack)
{
    return (haystack.Length - haystack.Replace(needle,"").Length) / needle.Length;
}

对于更长的针可能会更好......

但必须有更优雅的方式。 :)

【讨论】:

  • 考虑多字符替换。没有它,在“测试是关键”中计算“the”将返回 6。
  • 基准测试并将其与 string.Split-way 进行比较 - 工作速度大约快 1.5 倍。赞一个。
【解决方案9】:

编辑:

source.Split('/').Length-1

【讨论】:

  • 这就是我所做的。 source.Split(new[]{"//"}, StringSplitOptions.None).Count - 1 用于多字符分隔符。
  • 这将在堆上执行至少 n 个字符串分配,加上(可能)很少的数组重新大小 - 所有这些只是为了获得计数?效率极低,不能很好地扩展,不应该在任何重要的代码中使用。
【解决方案10】:
Regex.Matches(input,  Regex.Escape("stringToMatch")).Count

【讨论】:

  • 如果输入包含正则表达式特殊字符,则这是不正确的,即 |需要有一个 Regex.Escape(input)
  • 其实stringToMatch需要转义,而不是input
  • 没错。修好了。
【解决方案11】:

在 C# 中,一个不错的 String SubString 计数器就是这个出人意料的棘手家伙:

public static int CCount(String haystack, String needle)
{
    return haystack.Split(new[] { needle }, StringSplitOptions.None).Length - 1;
}

【讨论】:

  • 不错的解决方案 - 也适用于字符串(不仅仅是字符)!
  • 谢谢,在交换语言时很容易忘记字符串处理的一些微妙之处——就像我们现在大多数人一样!
  • -1 因为:您知道 Count() 和 Count 或 Length 之间的区别吗?如果有人使用 Count() 而不是 Count 或 Length 我会被触发。 Count() 创建 IEnumerator 然后遍历 IEnumerable 的所有出现,而 Count 或 Length 已经设置了对象的属性,这些属性已经保存了您想要的计数,而无需遍历所有元素。
  • 好地方,奇怪的是在我的库中,我从那里获取函数,我使用“长度”。已编辑!
  • 这个解决方案在aaaaaa中只找到了3次aa,而实际出现了5次
【解决方案12】:
private int CountWords(string text, string word) {
    int count = (text.Length - text.Replace(word, "").Length) / word.Length;
    return count;
}

因为最初的解决方案对于字符来说是最快的,我想它也适用于字符串。所以这是我的贡献。

对于上下文:我在日志文件中寻找“失败”和“成功”之类的词。

格, 本

【讨论】:

  • 只是不要为“word”变量传递一个空字符串(除以零错误)。
【解决方案13】:
string s = "65 fght 6565 4665 hjk";
int count = 0;
foreach (Match m in Regex.Matches(s, "65"))
  count++;

【讨论】:

  • 或 Regex.Matches(s, "65").Count ^_^
  • 不适用于每个字符串。尝试在“abc++def++xyz”中搜索“++”
【解决方案14】:

对于任何想要一个随时可用的字符串扩展方法的人,

这是我根据已发布的最佳答案使用的:

public static class StringExtension
{    
    /// <summary> Returns the number of occurences of a string within a string, optional comparison allows case and culture control. </summary>
    public static int Occurrences(this System.String input, string value, StringComparison stringComparisonType = StringComparison.Ordinal)
    {
        if (String.IsNullOrEmpty(value)) return 0;

        int count    = 0;
        int position = 0;

        while ((position = input.IndexOf(value, position, stringComparisonType)) != -1)
        {
            position += value.Length;
            count    += 1;
        }

        return count;
    }

    /// <summary> Returns the number of occurences of a single character within a string. </summary>
    public static int Occurrences(this System.String input, char value)
    {
        int count = 0;
        foreach (char c in input) if (c == value) count += 1;
        return count;
    }
}

【讨论】:

  • 如果传入的字符串为null或空,第二种方法会不会繁荣?纯粹从风格的角度来看,您将输入定义为 System.String 而不仅仅是字符串?
【解决方案15】:
public static int GetNumSubstringOccurrences(string text, string search)
{
    int num = 0;
    int pos = 0;

    if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(search))
    {
        while ((pos = text.IndexOf(search, pos)) > -1)
        {
            num ++;
            pos += search.Length;
        }
    }
    return num;
}

【讨论】:

    【解决方案16】:

    我认为最简单的方法是使用正则表达式。这样,您可以获得与使用 myVar.Split('x') 相同的拆分计数,但在多字符设置中。

    string myVar = "do this to count the number of words in my wording so that I can word it up!";
    int count = Regex.Split(myVar, "word").Length;
    

    【讨论】:

      【解决方案17】:

      从 .NET 5(Net core 2.1+ 和 NetStandard 2.1)开始,我们有了一个新的迭代速度之王。

      “跨度https://docs.microsoft.com/en-us/dotnet/api/system.span-1?view=net-5.0

      String 有一个内置的成员,它返回给我们一个 Span

      int count = 0;
      foreach( var c in source.AsSpan())
      {
          if (c == '/')
              count++;
      }
      

      我的测试显示比直接 foreach 快 62%。我还比较了 Span[i] 上的 for() 循环,以及此处发布的其他一些循环。请注意,String 上的反向 for() 迭代现在似乎比直接 foreach 运行得慢。

      Starting test, 10000000 iterations
      (base) foreach =   673 ms
      
      fastest to slowest
      foreach Span =   252 ms   62.6%
        Span [i--] =   282 ms   58.1%
        Span [i++] =   402 ms   40.3%
         for [i++] =   454 ms   32.5%
         for [i--] =   867 ms  -28.8%
           Replace =  1905 ms -183.1%
             Split =  2109 ms -213.4%
        Linq.Count =  3797 ms -464.2%
      

      更新:2021 年 12 月,Visual Studio 2022,.NET 5 和 6

      .NET 5
      Starting test, 100000000 iterations set
      (base) foreach =  7658 ms
      fastest to slowest
        foreach Span =   3710 ms     51.6%
          Span [i--] =   3745 ms     51.1%
          Span [i++] =   3932 ms     48.7%
           for [i++] =   4593 ms     40.0%
           for [i--] =   7042 ms      8.0%
      (base) foreach =   7658 ms      0.0%
             Replace =  18641 ms   -143.4%
               Split =  21469 ms   -180.3%
                Linq =  39726 ms   -418.8%
      Regex Compiled = 128422 ms -1,577.0%
               Regex = 179603 ms -2,245.3%
               
               
      .NET 6
      Starting test, 100000000 iterations set
      (base) foreach =  7343 ms
      fastest to slowest
        foreach Span =   2918 ms     60.3%
           for [i++] =   2945 ms     59.9%
          Span [i++] =   3105 ms     57.7%
          Span [i--] =   5076 ms     30.9%
      (base) foreach =   7343 ms      0.0%
           for [i--] =   8645 ms    -17.7%
             Replace =  18307 ms   -149.3%
               Split =  21440 ms   -192.0%
                Linq =  39354 ms   -435.9%
      Regex Compiled = 114178 ms -1,454.9%
               Regex = 186493 ms -2,439.7%
      

      我添加了更多循环并加入了 RegEx,因此我们可以看到在大量迭代中使用它是多么灾难。 我认为 for(++) 循环比较可能已经在 .NET 6 中进行了优化,以便在内部使用 Span - 因为它的速度几乎与 foreach span 相同。

      Code Link

      【讨论】:

      • 不错!这真的很酷,我几乎觉得这应该是新接受的答案!
      • @inspite 感谢您的投票,我想这就是您回答 12 岁问题时得到的结果。在找到 Span 之前我先来了这里,我想我会更新它。
      • Linq 方法到底为什么这么慢?我很好奇这对长字符串和短字符串有何变化。
      • @GarrGodfrey,我并没有“那么”震惊。我不认为 Linq 是为 10,000,000 次迭代的超紧循环而设计的......无论如何,如果你想测试它,我留下了一个代码链接。
      • 慢于Split 让我感到惊讶,因为这会创建一堆新字符串,而 Linq 应该只是在读取。必须是每个字符的函数调用。
      【解决方案18】:
      string search = "/string";
      var occurrences = (regex.Match(search, @"\/")).Count;
      

      每次程序准确找到“/s”(区分大小写)和 this 的出现次数将存储在变量“occurrences”中

      【讨论】:

        【解决方案19】:

        我觉得我们缺少某些类型的子字符串计数,例如不安全的逐字节比较。我把原海报的方法和我能想到的任何方法放在一起。

        这些是我做的字符串扩展。

        namespace Example
        {
            using System;
            using System.Text;
        
            public static class StringExtensions
            {
                public static int CountSubstr(this string str, string substr)
                {
                    return (str.Length - str.Replace(substr, "").Length) / substr.Length;
                }
        
                public static int CountSubstr(this string str, char substr)
                {
                    return (str.Length - str.Replace(substr.ToString(), "").Length);
                }
        
                public static int CountSubstr2(this string str, string substr)
                {
                    int substrlen = substr.Length;
                    int lastIndex = str.IndexOf(substr, 0, StringComparison.Ordinal);
                    int count = 0;
                    while (lastIndex != -1)
                    {
                        ++count;
                        lastIndex = str.IndexOf(substr, lastIndex + substrlen, StringComparison.Ordinal);
                    }
        
                    return count;
                }
        
                public static int CountSubstr2(this string str, char substr)
                {
                    int lastIndex = str.IndexOf(substr, 0);
                    int count = 0;
                    while (lastIndex != -1)
                    {
                        ++count;
                        lastIndex = str.IndexOf(substr, lastIndex + 1);
                    }
        
                    return count;
                }
        
                public static int CountChar(this string str, char substr)
                {
                    int length = str.Length;
                    int count = 0;
                    for (int i = 0; i < length; ++i)
                        if (str[i] == substr)
                            ++count;
        
                    return count;
                }
        
                public static int CountChar2(this string str, char substr)
                {
                    int count = 0;
                    foreach (var c in str)
                        if (c == substr)
                            ++count;
        
                    return count;
                }
        
                public static unsafe int CountChar3(this string str, char substr)
                {
                    int length = str.Length;
                    int count = 0;
                    fixed (char* chars = str)
                    {
                        for (int i = 0; i < length; ++i)
                            if (*(chars + i) == substr)
                                ++count;
                    }
        
                    return count;
                }
        
                public static unsafe int CountChar4(this string str, char substr)
                {
                    int length = str.Length;
                    int count = 0;
                    fixed (char* chars = str)
                    {
                        for (int i = length - 1; i >= 0; --i)
                            if (*(chars + i) == substr)
                                ++count;
                    }
        
                    return count;
                }
        
                public static unsafe int CountSubstr3(this string str, string substr)
                {
                    int length = str.Length;
                    int substrlen = substr.Length;
                    int count = 0;
                    fixed (char* strc = str)
                    {
                        fixed (char* substrc = substr)
                        {
                            int n = 0;
        
                            for (int i = 0; i < length; ++i)
                            {
                                if (*(strc + i) == *(substrc + n))
                                {
                                    ++n;
                                    if (n == substrlen)
                                    {
                                        ++count;
                                        n = 0;
                                    }
                                }
                                else
                                    n = 0;
                            }
                        }
                    }
        
                    return count;
                }
        
                public static int CountSubstr3(this string str, char substr)
                {
                    return CountSubstr3(str, substr.ToString());
                }
        
                public static unsafe int CountSubstr4(this string str, string substr)
                {
                    int length = str.Length;
                    int substrLastIndex = substr.Length - 1;
                    int count = 0;
                    fixed (char* strc = str)
                    {
                        fixed (char* substrc = substr)
                        {
                            int n = substrLastIndex;
        
                            for (int i = length - 1; i >= 0; --i)
                            {
                                if (*(strc + i) == *(substrc + n))
                                {
                                    if (--n == -1)
                                    {
                                        ++count;
                                        n = substrLastIndex;
                                    }
                                }
                                else
                                    n = substrLastIndex;
                            }
                        }
                    }
        
                    return count;
                }
        
                public static int CountSubstr4(this string str, char substr)
                {
                    return CountSubstr4(str, substr.ToString());
                }
            }
        }
        

        后面是测试代码...

        static void Main()
        {
            const char matchA = '_';
            const string matchB = "and";
            const string matchC = "muchlongerword";
            const string testStrA = "_and_d_e_banna_i_o___pfasd__and_d_e_banna_i_o___pfasd_";
            const string testStrB = "and sdf and ans andeians andano ip and and sdf and ans andeians andano ip and";
            const string testStrC =
                "muchlongerword amuchlongerworsdfmuchlongerwordsdf jmuchlongerworijv muchlongerword sdmuchlongerword dsmuchlongerword";
            const int testSize = 1000000;
            Console.WriteLine(testStrA.CountSubstr('_'));
            Console.WriteLine(testStrA.CountSubstr2('_'));
            Console.WriteLine(testStrA.CountSubstr3('_'));
            Console.WriteLine(testStrA.CountSubstr4('_'));
            Console.WriteLine(testStrA.CountChar('_'));
            Console.WriteLine(testStrA.CountChar2('_'));
            Console.WriteLine(testStrA.CountChar3('_'));
            Console.WriteLine(testStrA.CountChar4('_'));
            Console.WriteLine(testStrB.CountSubstr("and"));
            Console.WriteLine(testStrB.CountSubstr2("and"));
            Console.WriteLine(testStrB.CountSubstr3("and"));
            Console.WriteLine(testStrB.CountSubstr4("and"));
            Console.WriteLine(testStrC.CountSubstr("muchlongerword"));
            Console.WriteLine(testStrC.CountSubstr2("muchlongerword"));
            Console.WriteLine(testStrC.CountSubstr3("muchlongerword"));
            Console.WriteLine(testStrC.CountSubstr4("muchlongerword"));
            var timer = new Stopwatch();
            timer.Start();
            for (int i = 0; i < testSize; ++i)
                testStrA.CountSubstr(matchA);
            timer.Stop();
            Console.WriteLine("CS1 chr: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrB.CountSubstr(matchB);
            timer.Stop();
            Console.WriteLine("CS1 and: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrC.CountSubstr(matchC);
            timer.Stop();
            Console.WriteLine("CS1 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrA.CountSubstr2(matchA);
            timer.Stop();
            Console.WriteLine("CS2 chr: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrB.CountSubstr2(matchB);
            timer.Stop();
            Console.WriteLine("CS2 and: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrC.CountSubstr2(matchC);
            timer.Stop();
            Console.WriteLine("CS2 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrA.CountSubstr3(matchA);
            timer.Stop();
            Console.WriteLine("CS3 chr: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrB.CountSubstr3(matchB);
            timer.Stop();
            Console.WriteLine("CS3 and: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrC.CountSubstr3(matchC);
            timer.Stop();
            Console.WriteLine("CS3 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrA.CountSubstr4(matchA);
            timer.Stop();
            Console.WriteLine("CS4 chr: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrB.CountSubstr4(matchB);
            timer.Stop();
            Console.WriteLine("CS4 and: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrC.CountSubstr4(matchC);
            timer.Stop();
            Console.WriteLine("CS4 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrA.CountChar(matchA);
            timer.Stop();
            Console.WriteLine("CC1 chr: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrA.CountChar2(matchA);
            timer.Stop();
            Console.WriteLine("CC2 chr: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrA.CountChar3(matchA);
            timer.Stop();
            Console.WriteLine("CC3 chr: " + timer.Elapsed.TotalMilliseconds + "ms");
        
            timer.Restart();
            for (int i = 0; i < testSize; ++i)
                testStrA.CountChar4(matchA);
            timer.Stop();
            Console.WriteLine("CC4 chr: " + timer.Elapsed.TotalMilliseconds + "ms");
        }
        

        结果:CSX 对应 CountSubstrX,CCX 对应 CountCharX。 “chr”在字符串中搜索“_”,“and”在字符串中搜索“and”,“mlw”在字符串中搜索“muchlongerword”

        CS1 chr: 824.123ms
        CS1 and: 586.1893ms
        CS1 mlw: 486.5414ms
        CS2 chr: 127.8941ms
        CS2 and: 806.3918ms
        CS2 mlw: 497.318ms
        CS3 chr: 201.8896ms
        CS3 and: 124.0675ms
        CS3 mlw: 212.8341ms
        CS4 chr: 81.5183ms
        CS4 and: 92.0615ms
        CS4 mlw: 116.2197ms
        CC1 chr: 66.4078ms
        CC2 chr: 64.0161ms
        CC3 chr: 65.9013ms
        CC4 chr: 65.8206ms
        

        最后,我有一个包含 360 万个字符的文件。它是“derp adfderdserp dfaerpderp deasderp”重复了 100,000 次。我用上述方法在文件中搜索了“derp”,结果是这些结果的 100 倍。

        CS1Derp: 1501.3444ms
        CS2Derp: 1585.797ms
        CS3Derp: 376.0937ms
        CS4Derp: 271.1663ms
        

        所以我的第 4 种方法绝对是赢家,但实际上,如果一个 360 万字符的文件 100 次只用了 1586ms 作为最坏的情况,那么所有这些都可以忽略不计。

        顺便说一句,我还用 100 次 CountSubstr 和 CountChar 方法扫描了 360 万个字符文件中的 'd' 字符。结果...

        CS1  d : 2606.9513ms
        CS2  d : 339.7942ms
        CS3  d : 960.281ms
        CS4  d : 233.3442ms
        CC1  d : 302.4122ms
        CC2  d : 280.7719ms
        CC3  d : 299.1125ms
        CC4  d : 292.9365ms
        

        照着这个,原来的海报方法对于大海捞针的单字是很不好的。

        注意:所有值都已更新为发布版本输出。第一次发布此内容时,我不小心忘记了在发布模式下进行构建。我的一些陈述已被修改。

        【讨论】:

        • 感谢您提供的性能结果。速度差异 10 可能是不考虑使用 linq 或其他简洁编写的解决方案而是使用扩展方法的原因。
        【解决方案20】:

        字符串出现的通用函数:

        public int getNumberOfOccurencies(String inputString, String checkString)
        {
            if (checkString.Length > inputString.Length || checkString.Equals("")) { return 0; }
            int lengthDifference = inputString.Length - checkString.Length;
            int occurencies = 0;
            for (int i = 0; i < lengthDifference; i++) {
                if (inputString.Substring(i, checkString.Length).Equals(checkString)) { occurencies++; i += checkString.Length - 1; } }
            return occurencies;
        }
        

        【讨论】:

        • 这会创建大量的临时字符串并使垃圾收集器工作得非常辛苦。
        【解决方案21】:
        string source = "/once/upon/a/time/";
        int count = 0, n = 0;
        while ((n = source.IndexOf('/', n) + 1) != 0) count++;
        

        Richard Watson 答案的变体,char 出现在字符串中的次数越多,代码越少,效率越高!

        虽然我必须说,在没有对每个场景进行广泛测试的情况下,我确实看到使用以下方法显着提高了速度:

        int count = 0;
        for (int n = 0; n < source.Length; n++) if (source[n] == '/') count++;
        

        【讨论】:

          【解决方案22】:
                      var conditionalStatement = conditionSetting.Value;
          
                      //order of replace matters, remove == before =, incase of ===
                      conditionalStatement = conditionalStatement.Replace("==", "~").Replace("!=", "~").Replace('=', '~').Replace('!', '~').Replace('>', '~').Replace('<', '~').Replace(">=", "~").Replace("<=", "~");
          
                      var listOfValidConditions = new List<string>() { "!=", "==", ">", "<", ">=", "<=" };
          
                      if (conditionalStatement.Count(x => x == '~') != 1)
                      {
                          result.InvalidFieldList.Add(new KeyFieldData(batch.DECurrentField, "The IsDoubleKeyCondition does not contain a supported conditional statement. Contact System Administrator."));
                          result.Status = ValidatorStatus.Fail;
                          return result;
                      }
          

          需要做一些类似于测试字符串中的条件语句的事情。

          用单个字符替换我正在寻找的内容并计算单个字符的实例。

          显然,在发生这种情况之前,需要检查您正在使用的单个字符是否不存在于字符串中,以避免错误计数。

          【讨论】:

            【解决方案23】:

            字符串中的字符串:

            在“.. JD JD JD JD etc. and etc. JDJDJDJDJDJDJDJ and etc.”中找到“etc”

            var strOrigin = " .. JD JD JD JD etc. and etc. JDJDJDJDJDJDJDJD and etc.";
            var searchStr = "etc";
            int count = (strOrigin.Length - strOrigin.Replace(searchStr, "").Length)/searchStr.Length.
            

            在将其丢弃为不健全/笨拙之前检查性能...

            【讨论】:

              【解决方案24】:

              我最初的看法是这样的:

              public static int CountOccurrences(string original, string substring)
              {
                  if (string.IsNullOrEmpty(substring))
                      return 0;
                  if (substring.Length == 1)
                      return CountOccurrences(original, substring[0]);
                  if (string.IsNullOrEmpty(original) ||
                      substring.Length > original.Length)
                      return 0;
                  int substringCount = 0;
                  for (int charIndex = 0; charIndex < original.Length; charIndex++)
                  {
                      for (int subCharIndex = 0, secondaryCharIndex = charIndex; subCharIndex < substring.Length && secondaryCharIndex < original.Length; subCharIndex++, secondaryCharIndex++)
                      {
                          if (substring[subCharIndex] != original[secondaryCharIndex])
                              goto continueOuter;
                      }
                      if (charIndex + substring.Length > original.Length)
                          break;
                      charIndex += substring.Length - 1;
                      substringCount++;
                  continueOuter:
                      ;
                  }
                  return substringCount;
              }
              
              public static int CountOccurrences(string original, char @char)
              {
                  if (string.IsNullOrEmpty(original))
                      return 0;
                  int substringCount = 0;
                  for (int charIndex = 0; charIndex < original.Length; charIndex++)
                      if (@char == original[charIndex])
                          substringCount++;
                  return substringCount;
              }
              

              使用替换和除法的大海捞针方法产生 21 多秒,而这大约需要 15.2 秒。

              在添加一点将substring.Length - 1 添加到 charIndex 后进行编辑(就像它应该的那样),它是 11.6 秒。

              编辑 2:我使用了一个包含 26 个双字符字符串的字符串,以下是更新为相同示例文本的时间:

              大海捞针(OP 版本):7.8 秒

              建议机制:4.6 秒。

              编辑 3:添加单个字符的角盒,它达到了 1.2 秒。

              编辑 4:对于上下文:使用了 5000 万次迭代。

              【讨论】:

                【解决方案25】:

                我想我会把我的扩展方法扔进环中(有关更多信息,请参阅 cmets)。我没有做过任何正式的基准测试,但我认为在大多数情况下它必须非常快。

                编辑:好的 - 所以这个 SO 问题让我想知道我们当前实现的性能将如何与此处介绍的一些解决方案相提并论。我决定做一些基准测试,发现我们的解决方案非常符合 Richard Watson 提供的解决方案的性能,直到您使用大字符串 (100 Kb +)、大子字符串 (32 Kb +) 和许多嵌入的重复 (10K +)。那时我们的解决方案慢了大约 2 到 4 倍。鉴于这一点以及我们非常喜欢 Richard Watson 提出的解决方案这一事实,我们相应地重构了我们的解决方案。我只是想让任何可能从中受益的人都可以使用它。

                我们最初的解决方案:

                    /// <summary>
                    /// Counts the number of occurrences of the specified substring within
                    /// the current string.
                    /// </summary>
                    /// <param name="s">The current string.</param>
                    /// <param name="substring">The substring we are searching for.</param>
                    /// <param name="aggressiveSearch">Indicates whether or not the algorithm 
                    /// should be aggressive in its search behavior (see Remarks). Default 
                    /// behavior is non-aggressive.</param>
                    /// <remarks>This algorithm has two search modes - aggressive and 
                    /// non-aggressive. When in aggressive search mode (aggressiveSearch = 
                    /// true), the algorithm will try to match at every possible starting 
                    /// character index within the string. When false, all subsequent 
                    /// character indexes within a substring match will not be evaluated. 
                    /// For example, if the string was 'abbbc' and we were searching for 
                    /// the substring 'bb', then aggressive search would find 2 matches 
                    /// with starting indexes of 1 and 2. Non aggressive search would find 
                    /// just 1 match with starting index at 1. After the match was made, 
                    /// the non aggressive search would attempt to make it's next match 
                    /// starting at index 3 instead of 2.</remarks>
                    /// <returns>The count of occurrences of the substring within the string.</returns>
                    public static int CountOccurrences(this string s, string substring, 
                        bool aggressiveSearch = false)
                    {
                        // if s or substring is null or empty, substring cannot be found in s
                        if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(substring))
                            return 0;
                
                        // if the length of substring is greater than the length of s,
                        // substring cannot be found in s
                        if (substring.Length > s.Length)
                            return 0;
                
                        var sChars = s.ToCharArray();
                        var substringChars = substring.ToCharArray();
                        var count = 0;
                        var sCharsIndex = 0;
                
                        // substring cannot start in s beyond following index
                        var lastStartIndex = sChars.Length - substringChars.Length;
                
                        while (sCharsIndex <= lastStartIndex)
                        {
                            if (sChars[sCharsIndex] == substringChars[0])
                            {
                                // potential match checking
                                var match = true;
                                var offset = 1;
                                while (offset < substringChars.Length)
                                {
                                    if (sChars[sCharsIndex + offset] != substringChars[offset])
                                    {
                                        match = false;
                                        break;
                                    }
                                    offset++;
                                }
                                if (match)
                                {
                                    count++;
                                    // if aggressive, just advance to next char in s, otherwise, 
                                    // skip past the match just found in s
                                    sCharsIndex += aggressiveSearch ? 1 : substringChars.Length;
                                }
                                else
                                {
                                    // no match found, just move to next char in s
                                    sCharsIndex++;
                                }
                            }
                            else
                            {
                                // no match at current index, move along
                                sCharsIndex++;
                            }
                        }
                
                        return count;
                    }
                

                这是我们修改后的解决方案:

                    /// <summary>
                    /// Counts the number of occurrences of the specified substring within
                    /// the current string.
                    /// </summary>
                    /// <param name="s">The current string.</param>
                    /// <param name="substring">The substring we are searching for.</param>
                    /// <param name="aggressiveSearch">Indicates whether or not the algorithm 
                    /// should be aggressive in its search behavior (see Remarks). Default 
                    /// behavior is non-aggressive.</param>
                    /// <remarks>This algorithm has two search modes - aggressive and 
                    /// non-aggressive. When in aggressive search mode (aggressiveSearch = 
                    /// true), the algorithm will try to match at every possible starting 
                    /// character index within the string. When false, all subsequent 
                    /// character indexes within a substring match will not be evaluated. 
                    /// For example, if the string was 'abbbc' and we were searching for 
                    /// the substring 'bb', then aggressive search would find 2 matches 
                    /// with starting indexes of 1 and 2. Non aggressive search would find 
                    /// just 1 match with starting index at 1. After the match was made, 
                    /// the non aggressive search would attempt to make it's next match 
                    /// starting at index 3 instead of 2.</remarks>
                    /// <returns>The count of occurrences of the substring within the string.</returns>
                    public static int CountOccurrences(this string s, string substring, 
                        bool aggressiveSearch = false)
                    {
                        // if s or substring is null or empty, substring cannot be found in s
                        if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(substring))
                            return 0;
                
                        // if the length of substring is greater than the length of s,
                        // substring cannot be found in s
                        if (substring.Length > s.Length)
                            return 0;
                
                        int count = 0, n = 0;
                        while ((n = s.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
                        {
                            if (aggressiveSearch)
                                n++;
                            else
                                n += substring.Length;
                            count++;
                        }
                
                        return count;
                    }
                

                【讨论】:

                  【解决方案26】:
                  string Name = "Very good nice one is very good but is very good nice one this is called the term";
                  bool valid=true;
                  int count = 0;
                  int k=0;
                  int m = 0;
                  while (valid)
                  {
                      k = Name.Substring(m,Name.Length-m).IndexOf("good");
                      if (k != -1)
                      {
                          count++;
                          m = m + k + 4;
                      }
                      else
                          valid = false;
                  }
                  Console.WriteLine(count + " Times accures");
                  

                  【讨论】:

                    【解决方案27】:

                    如果您check out this webpage,则对 15 种不同的方法进行基准测试,包括使用并行循环。

                    最快的方法似乎是使用单线程 for 循环(如果您的 .Net 版本 4.0)。

                    假设“ss”是您的搜索字符串,“ch”是您的字符数组(如果您要查找的字符不止一个),以下是单线程运行时间最快的代码的基本要点:

                    for (int x = 0; x < ss.Length; x++)
                    {
                        for (int y = 0; y < ch.Length; y++)
                        {
                            for (int a = 0; a < ss[x].Length; a++ )
                            {
                            if (ss[x][a] == ch[y])
                                //it's found. DO what you need to here.
                            }
                        }
                    }
                    

                    还提供了基准源代码,因此您可以运行自己的测试。

                    【讨论】:

                      【解决方案28】:
                      str="aaabbbbjjja";
                      int count = 0;
                      int size = str.Length;
                      
                      string[] strarray = new string[size];
                      for (int i = 0; i < str.Length; i++)
                      {
                          strarray[i] = str.Substring(i, 1);
                      }
                      Array.Sort(strarray);
                      str = "";
                      for (int i = 0; i < strarray.Length - 1; i++)
                      {
                      
                          if (strarray[i] == strarray[i + 1])
                          {
                      
                              count++;
                          }
                          else
                          {
                              count++;
                              str = str + strarray[i] + count;
                              count = 0;
                          }
                      
                      }
                      count++;
                      str = str + strarray[strarray.Length - 1] + count;
                      

                      这是用于计算字符出现次数。对于此示例,输出将为“a4b4j3”

                      【讨论】:

                      • 不完全“计算字符串的出现次数”更多计数字符 - 指定要匹配的字符串是 Narenda 的方法怎么样?
                      • int count = 0; string str = "我们有 foo 和 foo 请把 foo 算进去";字符串 strocurance="foo"; string[] strarray = str.Split(' '); Array.Sort(strarray); str = ""; for (int i = 0; i
                      【解决方案29】:
                      string s = "HOWLYH THIS ACTUALLY WORKSH WOWH";
                      int count = 0;
                      for (int i = 0; i < s.Length; i++)
                         if (s[i] == 'H') count++;
                      

                      它只是检查字符串中的每个字符,如果该字符是您要搜索的字符,则加一计数。

                      【讨论】:

                        【解决方案30】:

                        对于字符串分隔符的情况(不是针对字符的情况,如主题所说):
                        字符串源 = "@@@once@@@upon@@@a@@@time@@@";
                        int count = source.Split(new[] { "@@@" }, StringSplitOptions.RemoveEmptyEntries).Length - 1;

                        发帖人的原始来源值 ("/once/upon/a/time/") 自然分隔符是 char '/' 并且响应确实解释了 source.Split(char[]) 选项...

                        【讨论】:

                          猜你喜欢
                          • 2012-10-03
                          • 2014-04-24
                          • 2010-09-21
                          • 2020-02-21
                          • 2012-02-12
                          • 1970-01-01
                          相关资源
                          最近更新 更多