我正在做一些事情,我意识到我想要在一个字符串中找到多少个/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;
我做了一些研究,发现理查德·沃森的解决方案在大多数情况下是最快的。这是文章中每个解决方案的结果表(除了那些使用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;
}
}
Split (may)胜过IndexOf(用于字符串)。
上面的基准测试似乎表明Richard Watson是最快的字符串,这是错误的(可能差异来自我们的测试数据,但由于下面的原因,它看起来很奇怪)。
如果我们更深入地研究这些方法在.NET中的实现(对于Luke H, Richard Watson方法),
IndexOf取决于区域性,它将尝试检索/创建ReadOnlySpan,检查是否必须忽略大小写等。最后执行不安全/本机调用。
Split能够处理多个分隔符,并有一些StringSplitOptions
并且必须创建字符串[]数组并用分割结果填充它(所以做一些子字符串)。根据字符串出现的数量,Split可能比IndexOf更快。
顺便说一下,我做了一个简化版本的IndexOf(它可以更快,如果我使用指针和不安全,但不勾选应该是ok的大多数),它至少快了4个数量级。
基准测试(来源GitHub)
通过搜索一个常见的单词(the)或一个小句子
莎士比亚,理查三世。
Method |
Mean |
Error |
StdDev |
Ratio |
Richard_LongInLong |
67.721 us |
1.0278 us |
0.9614 us |
1.00 |
Luke_LongInLong |
1.960 us |
0.0381 us |
0.0637 us |
0.03 |
Fab_LongInLong |
1.198 us |
0.0160 us |
0.0142 us |
0.02 |
-------------------- |
-----------: |
----------: |
----------: |
------: |
Richard_ShortInLong |
104.771 us |
2.8117 us |
7.9304 us |
1.00 |
Luke_ShortInLong |
2.971 us |
0.0594 us |
0.0813 us |
0.03 |
Fab_ShortInLong |
2.206 us |
0.0419 us |
0.0411 us |
0.02 |
--------------------- |
----------: |
---------: |
---------: |
------: |
Richard_ShortInShort |
115.53 ns |
1.359 ns |
1.135 ns |
1.00 |
Luke_ShortInShort |
52.46 ns |
0.970 ns |
0.908 ns |
0.45 |
Fab_ShortInShort |
28.47 ns |
0.552 ns |
0.542 ns |
0.25 |
public int GetOccurrences(string input, string needle)
{
int count = 0;
unchecked
{
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(needle))
{
return 0;
}
for (var i = 0; i < input.Length - needle.Length + 1; i++)
{
var c = input[i];
if (c == needle[0])
{
for (var index = 0; index < needle.Length; index++)
{
c = input[i + index];
var n = needle[index];
if (c != n)
{
break;
}
else if (index == needle.Length - 1)
{
count++;
}
}
}
}
}
return count;
}
查找字符计数与查找字符串计数有很大不同。另外,这也取决于你是否想要检查不止一个。如果你想检查各种不同的字符计数,像这样的东西可以工作:
var charCounts =
haystack
.GroupBy(c => c)
.ToDictionary(g => g.Key, g => g.Count());
var needleCount = charCounts.ContainsKey(needle) ? charCounts[needle] : 0;
注1:分组到字典中非常有用,因此为它编写GroupToDictionary扩展方法非常有意义。
注意2:拥有自己的字典实现也很有用,它允许默认值,然后您可以自动为不存在的键获取0。