我正在做一些事情,我意识到我想要计算/我能在字符串中找到多少,然后它让我感到震惊,有几种方法可以做到,但无法决定最好的(或最简单的)是什么.
目前我正在做的事情如下:
string source = "/once/upon/a/time/";
int count = source.Length - source.Replace("/", "").Length;
Run Code Online (Sandbox Code Playgroud)
但我完全不喜欢它,任何接受者?
我真的不想挖掘RegEx这个,是吗?
我知道我的字符串将会有我正在搜索的术语,所以你可以认为......
当然,对于字符串,其中 长度> 1,
string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = ( haystack.Length - haystack.Replace(needle,"").Length ) / needle.Length;
Run Code Online (Sandbox Code Playgroud)
Luk*_*keH 955
如果您使用的是.NET 3.5,则可以使用LINQ在单行中执行此操作:
int count = source.Count(f => f == '/');
Run Code Online (Sandbox Code Playgroud)
如果您不想使用LINQ,可以使用以下命令:
int count = source.Split('/').Length - 1;
Run Code Online (Sandbox Code Playgroud)
您可能会惊讶地发现,您的原始技术似乎比其中任何一种快约30%!我刚用"/ once/on/a/time /"做了快速基准测试,结果如下:
你的原始= 12s source.Count
= 19s
source.Split = 17s
foreach(来自bobwienholt的回答)= 10s
(时间是50,000,000次迭代,因此您不太可能注意到现实世界中的差异.)
bob*_*olt 171
string source = "/once/upon/a/time/";
int count = 0;
foreach (char c in source)
if (c == '/') count++;
Run Code Online (Sandbox Code Playgroud)
必须比source.Replace()自己快.
Yet*_*ker 130
int count = new Regex(Regex.Escape(needle)).Matches(haystack).Count;
Run Code Online (Sandbox Code Playgroud)
mqp*_*mqp 84
如果您希望能够搜索整个字符串,而不仅仅是字符:
src.Select((c, i) => src.Substring(i))
.Count(sub => sub.StartsWith(target))
Run Code Online (Sandbox Code Playgroud)
读作"对于字符串中的每个字符,将该字符的其余部分从该字符开始作为子字符串;如果它以目标字符串开头,则计算它."
tsi*_*nyx 62
我做了一些研究,发现Richard Watson的解决方案在大多数情况下都是最快的.这是包含帖子中每个解决方案结果的表(除了那些使用正则表达式,因为它在解析字符串时抛出异常,如"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|
Run Code Online (Sandbox Code Playgroud)
您可以看到,如果在短字符串(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;
}
}
Run Code Online (Sandbox Code Playgroud)
Jud*_*ngo 53
LINQ适用于所有集合,因为字符串只是一个字符集合,所以这个漂亮的小单行如何:
var count = source.Count(c => c == '/');
Run Code Online (Sandbox Code Playgroud)
确保您using System.Linq;位于代码文件的顶部,这.Count是来自该命名空间的扩展方法.
Ric*_*son 48
string source = "/once/upon/a/time/";
int count = 0;
int n = 0;
while ((n = source.IndexOf('/', n)) != -1)
{
n++;
count++;
}
Run Code Online (Sandbox Code Playgroud)
在我的计算机上,它比5000万次迭代的每个角色解决方案快约2秒.
2013年修订:
将字符串更改为char []并迭代它.将总时间缩短一两秒,进行50米迭代!
char[] testchars = source.ToCharArray();
foreach (char c in testchars)
{
if (c == '/')
count++;
}
Run Code Online (Sandbox Code Playgroud)
这更快:
char[] testchars = source.ToCharArray();
int length = testchars.Length;
for (int n = 0; n < length; n++)
{
if (testchars[n] == '/')
count++;
}
Run Code Online (Sandbox Code Playgroud)
为了更好地衡量,从数组末尾迭代到0似乎是最快的,大约5%.
int length = testchars.Length;
for (int n = length-1; n >= 0; n--)
{
if (testchars[n] == '/')
count++;
}
Run Code Online (Sandbox Code Playgroud)
我想知道为什么会这样,并且谷歌搜索(我记得有关反向迭代更快的事情),并且发现了这个烦人地使用字符串char []技术的问题.不过,我认为逆转技巧在这方面是新的.
Zom*_*eep 45
这些都只适用于单字符搜索术语......
countOccurences("the", "the answer is the answer");
int countOccurences(string needle, string haystack)
{
return (haystack.Length - haystack.Replace(needle,"").Length) / needle.Length;
}
Run Code Online (Sandbox Code Playgroud)
对于更长的针头可能会变得更好......
但必须有一种更优雅的方式.:)
Bri*_*lph 19
编辑:
source.Split('/').Length-1
Run Code Online (Sandbox Code Playgroud)
Dav*_*ave 15
在C#中,一个不错的String SubString计数器就是这个意想不到的棘手问题:
public static int CCount(String haystack, String needle)
{
return haystack.Split(new[] { needle }, StringSplitOptions.None).Length - 1;
}
Run Code Online (Sandbox Code Playgroud)
ced*_*lof 14
Regex.Matches(input, Regex.Escape("stringToMatch")).Count
Run Code Online (Sandbox Code Playgroud)
小智 12
private int CountWords(string text, string word) {
int count = (text.Length - text.Replace(word, "").Length) / word.Length;
return count;
}
Run Code Online (Sandbox Code Playgroud)
因为原始解决方案对于字符来说是最快的,我想它也适用于字符串.所以这是我的贡献.
对于上下文:我在日志文件中寻找"失败"和"成功"之类的单词.
Gr,Ben
小智 11
string s = "65 fght 6565 4665 hjk";
int count = 0;
foreach (Match m in Regex.Matches(s, "65"))
count++;
Run Code Online (Sandbox Code Playgroud)
从 .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++;
}
Run Code Online (Sandbox Code Playgroud)
我的测试显示比直接 foreach 快 62%。我还与 Span<T>[i] 上的 for() 循环以及此处发布的其他一些循环进行了比较。请注意,字符串上的反向 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%
Run Code Online (Sandbox Code Playgroud)
更新: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%
Run Code Online (Sandbox Code Playgroud)
我添加了更多循环并放入了正则表达式,这样我们就可以看到在大量迭代中使用它是多么的灾难。我认为 for(++) 循环比较可能已在 .NET 6 中进行了优化,以便在内部使用 Span - 因为它的速度几乎与 foreach span 相同。
从 .NET 7 开始,我们拥有免分配(且高度优化)的正则表达式 API。计数特别简单、高效。
var input = "abcd abcabc ababc";
var result = Regex.Count(input: input, pattern: "abc"); // 4
Run Code Online (Sandbox Code Playgroud)
当匹配动态模式时,记得转义它们:
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;
}
Run Code Online (Sandbox Code Playgroud)
而且,作为固定模式的奖励,.NET 7 引入了分析器,可以帮助将正则表达式字符串转换为源生成的代码。这不仅避免了正则表达式的运行时编译开销,而且还提供了非常可读的代码来显示它是如何实现的。事实上,该代码通常至少与您手动编写的任何替代代码一样高效。
如果您的正则表达式调用符合条件,分析器将给出提示。只需选择“转换为‘GenelatedRegexAttribute’”即可享受结果:
[GeneratedRegex("abc")]
private static partial Regex MyRegex(); // Go To Definition to see the generated code
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
对于任何想要使用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;
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我认为最简单的方法是使用正则表达式.这样,您可以获得与使用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;
Run Code Online (Sandbox Code Playgroud)