您如何计算字符串中字符串(实际上是字符)的出现?

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

答案

如果您使用的是. NET 3.5,则可以使用 LINQ 以单线方式执行此操作:

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

如果您不想使用 LINQ,可以使用以下方法:

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

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

你原来的 = 12 秒
source.Count = 19 秒
source.Split = 17 秒
foreach( 根据 bobwienholt 的回答 )= 10s

(时间是进行 5000 万次迭代,因此您不太可能注意到现实世界中的巨大差异。)

string source = "/once/upon/a/time/";
int count = 0;
foreach (char c in source) 
  if (c == '/') count++;

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

int count = new Regex(Regex.Escape(needle)).Matches(haystack).Count;

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

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

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

我进行了一些研究,发现Richard Watson 的解决方案在大多数情况下是最快的。这就是该表中包含每个解决方案的结果的表(那些使用Regex 的解决方案除外,因为使用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 的解决方案)

int count = 0, n = 0;

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

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

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

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

string source = "/once/upon/a/time/";
int count = 0;
int n = 0;

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

在我的计算机上,它比每个字符的解决方案都要快大约 2 秒,可以进行 5000 万次迭代。

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 问题,它已经烦人地使用了字符串 char [] 技术。我认为,在这种情况下,逆转技巧是新的。

在 C#中,迭代字符串中各个字符的最快方法是什么?

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

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

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

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

但是必须有一种更优雅的方法。 :)

编辑:

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

在 C#中,一个不错的 String SubString 计数器是这个异常棘手的家伙:

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