String.Contains如何工作?
可能重复:
什么算法.Net用于搜索字符串中的模式?
我的程序中有一个循环从文件中获取一行。 然后检查该行是否包含字符串
if(line.Contains("String")) { //Do other stuff }
文件中有超过200万行,所以如果我可以将速度加快1/10毫秒,那么每次运行会节省3分钟以上。
所以…假设一条线长1000个字符,是否更快找到一个短或长的字符串,或者它没有区别?
line.Contains("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
要么
line.Contains("ABCDEFG")
先感谢您。
String.Contains()遵循一条曲折的路线,通过System.Globalization.CompareInfo进入CLR和NLS支持子系统,在那里我彻底迷失了。 这包含高度优化的代码和令人印象深刻 更快地完成它的唯一方法是通过pingvvrt.dll中提供的标准CRT函数wcsstr
[DllImport("msvcrt.dll", CharSet = CharSet.Unicode)] private static extern IntPtr wcsstr(string toSearch, string toFind);
如果找不到该字符串,则返回IntPtr.Zero。 我做了一些测量,使用String.IndexOf()而不是Contains()来测试各种字符串比较选项。 所有时间都以纳秒为单位,在60个字符的字符串中搜索7个字符的字符串。 由于字符串不存在,因此测量最坏情况。 使用20个样本中的最短时间:
StringComparison.Ordinal (same as Contains) : 245 nanoseconds StringComparison.OrdinalIgnoreCase : 327 StringComparison.InvariantCulture : 251 StringComparison.InvariantCultureIgnoreCase : 327 StringComparison.CurrentCulture : 275 StringComparison.CurrentCultureIgnoreCase : 340 wcsstr : 213
非常令人印象深刻的数字,与您期望这些function所需要的相同。 wcsstr()函数与String.Compare()进行相同的序数比较。 由于CPU缓存局部性的影响,实际性能不太可能接近这些测量,因此它仅快13%,在统计上无显着改善。 我只能得出结论,你的速度和你想象的一样快。 wcsstr的微小改进是否值得,取决于你。
“通常,当搜索的密钥变得更长时,算法变得更快”
http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm
如果.NET String.Contains使用Boyer-Moore算法 ,则搜索更长的字符串会更快。
我可以建议,如果你正在阅读一个分隔文件,例如每行是一系列用逗号分隔的文本字段,你可以节省一些搜索时间,如果你知道它不能在行之前从行中的位置0搜索例如,角色40。
在分隔符字符上使用String Spilled函数分割一条线是很常见的,这会返回一个字符串数组。 然后,您只能在值可能出现的字段内搜索。 这也会更快。
在提高性能方面,您需要确保您想要改进的内容实际上是导致瓶颈的原因。 我将首先分析代码中各个部分的性能,然后找出哪个部分引入了最大的瓶颈。
乍一看,我看到这个过程的三个主要组成部分值得分析:
- 循环遍历文件的全部内容(这可能包括也可能不包括文件IO注意事项,您可能希望单独测试)。
- 检查文件中每一行的
String.Contains()
。 - 当有比赛时“Do Stuff”
虽然那里有很棒的商业分析器,但您可以在代码中使用简单的计时器(如System.Diagnostics.Stopwatch
),或者,如果过程非常长,只需使用watch来测量时间。
测量以下各项的时间。
- 测量只需查看整个文件而无需执行任何其他操作所需的时间。 这会隔离您的循环和IO代码,以便您可以看到它的执行情况。
- 接下来,仅添加字符串比较(
String.Contains
)并测量添加此字符串String.Contains
的总时间。 - 最后,添加“do stuff”代码并使用此添加来衡量总时间。
您现在可以比较每个组件中添加的成本,以确定该组件的成本以及是否需要对其进行改进。 例如,假设您录制了以下时间:
Test Total Time Cost (Difference) ============================================= Do Nothing 0s 0s Loop Only 100s 100s Add Comparison 105s 5s Add Do Stuff 130s 25s
看看那些(假的)数字,这个过程中最昂贵的部分是循环和IO代码,所以这就是我开始尝试提高性能的地方。 由于“Do Stuff”部分在整个执行时间上增加了25秒,我会看看旁边的代码,看看是否有什么我可以改进的。 最后,我会看一下字符串比较。
当然,我提供的时间测量值是虚构的,但您可以在应用程序中对性能分析应用相同的步骤。 关键是确定最大的成本,并尝试先减少成本。
令我惊讶的是,编译的Regex
实际上比Contains
快得多。 当然,如果您多次搜索相同的字符串,编译的成本是值得的。 但如果是这样,它的速度要快两倍以上。 亲自尝试一下:
static void Test() { var random = new Random(10); var alphabet = "abcdefghijklmnopqrstuvwxyz"; var content = new String((from x in Enumerable.Range(0, 10000000) select a[random.Next(0, a.Length)]).ToArray()); var searchString = content.Substring(5000000, 4096); var regex = new Regex(searchString); var sw = Stopwatch.StartNew(); for (int i = 0; i < 1000; i++) { if (!regex.IsMatch(content)) { throw new Exception(); } } sw.Stop(); Console.WriteLine("Regex: " + sw.Elapsed); sw.Restart(); for (int i = 0; i < 1000; i++) { if (!content.Contains(searchString)) { throw new Exception(); } } sw.Stop(); Console.WriteLine("String.Contains: " + sw.Elapsed); }
我仍然没有弄清楚它是如何快速的,看着编译的程序集,它是一个混乱的switch
语句混乱。 但它很快, 非常快。
当且仅当此字符串包含指定的char值序列时,String.Contains方法才返回true。
它主要用于检查字符串中的子字符串。
http://csharp.net-informations.com/string/csharp-string-contains.htm
像所有的字符串搜索方法(我认为)一样,包含最终会导致外部方法InternalFindNLSStringEx
– 无论如何,它都是相当不可思议的。