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

目前我想说的是:

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

但我一点都不喜欢,有人愿意吗?

我并不想为此挖掘出正则表达式,对吧?

我知道我的字符串将包含我要搜索的项,所以你可以假设…

当然对于长度为> 1的字符串,

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

当前回答

string s = "65 fght 6565 4665 hjk";
int count = 0;
foreach (Match m in Regex.Matches(s, "65"))
  count++;

其他回答

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

“跨度<T>”https://learn.microsoft.com/en-us/dotnet/api/system.span-1?view=net-5.0

String有一个内置成员,返回Span<Char>

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

我的测试显示比直刺快62%我还比较了Span<T>[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的速度几乎相同。

代码链接

对于任何想要使用String扩展方法的人,

以下是我使用的基于张贴的最好的答案:

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;
    }
}

从。net 7开始,我们就有了不需要分配(并且高度优化)的Regex api。计数尤其容易和有效。

    var input = "abcd abcabc ababc";
    var result = Regex.Count(input: input, pattern: "abc"); // 4

当匹配动态模式时,记得转义它们:

public static int CountOccurences(string input, string pattern)
{
    pattern = Regex.Escape(pattern); // Aww, no way to avoid heap allocations here

    var result = Regex.Count(input: input, pattern: pattern);
    return result;
}

而且,作为固定模式的额外奖励,. net 7引入了分析器,帮助将正则表达式字符串转换为源代码生成的代码。这不仅避免了regex的运行时编译开销,而且还提供了非常可读的代码,展示了它是如何实现的。事实上,该代码通常至少与手动编写的任何替代代码一样有效。

如果您的正则表达式调用是合格的,分析程序将给出提示。简单地选择“转换为'GeneratedRegexAttribute '”并享受结果:

[GeneratedRegex("abc")]
private static partial Regex MyRegex(); // Go To Definition to see the generated code

字符串中的字符串:

在“..”中找到“etc”。JD JD JD JD等等。JDJDJDJDJDJDJDJD等等。”

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.

在丢弃这个不健全/笨拙之前检查性能…

如果你使用的是。net 3.5,你可以用LINQ在一行代码中完成:

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

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

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

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

你的原稿= 12s 源。计数= 19秒 源。分裂= 17秒 Foreach(来自bobwienholt的答案)= 10s

(迭代次数为50,000,000次,因此在现实世界中您不太可能注意到太多差异。)