字母"a"在单词"abracadabra"中出现多少次?用C#解决这个问题
想象一下:你在编写日志分析器、解析CSV文件,或者只是检查用户在电子邮件中输入@符号的次数。"统计字符或子字符串在字符串中出现的次数" 这个任务是那种在每个项目里都会出现的问题。在Stack Overflow上,这个问题已经积累了数千次浏览和几十种答案选项。这并不奇怪:在C#中至少有5种不同的方法,选择正确的方法可以节省数小时的调试时间。
在本文中,我们将分解所有流行的方法——从经典的循环到现代的Span<T>和LINQ。你将了解哪种方法更快,哪种更具可读性,哪种最好不要在生产中使用。开始吧!
1. 经典的foreach循环——老派方法
最直观的方式:获取一个字符串,遍历每个字符,并统计匹配项。它始终有效,并且不需要额外的库。
string text = "abracadabra";char target = 'a';int count = 0;
foreach (char c in text){ if (c == target) count++;}
Console.WriteLine($"字符'{target}'出现了{count}次。"); // 输出: 4优点: 可读性强,过程可控,可以添加逻辑(例如,忽略大小写)。
缺点: 不适合统计子字符串(多个连续字符)——这需要编写嵌套循环。
2. LINQ中的Count()方法——针对字符的一行代码
如果你喜欢简洁的代码并且使用LINQ,那么这个选项适合你。Count()方法接受一个谓词,并返回满足条件的元素数量。
using System.Linq;
string text = "Hello, World!";char target = 'l';
int count = text.Count(c => c == target);Console.WriteLine(count); // 输出: 3注意:Count()是来自System.Linq命名空间的扩展方法。如果你没有添加using System.Linq,代码将无法编译。
这个方法并不直接适用于统计子字符串。你需要先分割字符串或使用正则表达式(参见第5节)。
3. Span<T> 和 MemoryExtensions —— 最大性能
如果你处理大型字符串(兆字节级别的日志),并且每一毫秒都很重要,那么忘记LINQ吧。使用Span<char>和来自MemoryExtensions的Count()方法(从.NET 5+开始可用)。
using System;
string text = "abracadabra";ReadOnlySpan<char> span = text.AsSpan();int count = span.Count('a');
Console.WriteLine(count); // 输出: 4为什么这么快? Span<T>处理连续内存块,无需分配新对象。MemoryExtensions中的Count()方法针对SIMD指令进行了优化——每个CPU周期可处理16-32个字符。
还有一个针对子字符串的解决方案:
int count = span.Count("ra"); // 统计子字符串 "ra" 出现的次数Console.WriteLine(count); // 输出: 2 ("abracadabra" 包含 "ra" 两次)重要提示:此方法区分大小写。如果你需要忽略大小写,请先将字符串转换为统一大小写,或使用 span.Count("ra", StringComparison.OrdinalIgnoreCase)。
4. Split方法——针对子字符串的巧妙技巧
一个古老但有效的方法:按目标子字符串分割字符串,然后统计结果部分的数量减一。
string text = "one, two, three, one, four";string target = "one";
int count = text.Split(target, StringSplitOptions.None).Length - 1;Console.WriteLine(count); // 输出: 2工作原理: Split