String.Contains如何工作?

时间:2010-09-22 13:08:46

标签: c# performance string

  

可能重复:
  What algorithm .Net use for searching a pattern in a string?

我的程序中有一个循环从文件中获取一行。然后检查该行是否包含字符串

if(line.Contains("String"))
{
    //Do other stuff
}

文件中有超过200万行,所以如果我可以将速度加快1/10毫秒,那么每次运行会节省3分钟。

所以...假设一条线是1000个字符长,是否可以更快地寻找一个短或长的字符串,或者它没有什么区别?

line.Contains("ABCDEFGHIJKLMNOPQRSTUVWXYZ");

line.Contains("ABCDEFG")

提前谢谢。

7 个答案:

答案 0 :(得分:23)

String.Contains()遵循一条曲折的路线,通过System.Globalization.CompareInfo进入CLR和NLS支持子系统,在那里我彻底迷失了。这包含高度优化的代码和令人印象深刻更快地完成它的唯一方法是通过调整标准CRT函数wcsstr,可在msvcrt.dll中找到

    [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

非常令人印象深刻的数字,与您期望这些功能所要求的相提并论。 wcsstr()函数与String.Compare()执行相同类型的序数比较。由于CPU缓存局部性的影响,实际性能不太可能接近这些测量,因此它仅快13%,在统计上无显着改善。我只能得出结论,你的速度和你想象的一样快。 wcsstr的微小改进是否值得,取决于你。

答案 1 :(得分:7)

“通常,当搜索的密钥变得更长时,算法变得更快”

http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm

答案 2 :(得分:3)

如果.NET String.Contains使用Boyer-Moore algorithm,搜索更长的字符串会更快。

我可以建议,如果您正在阅读分隔文件,例如每行是一系列以逗号分隔的文本字段,您可以通过不从行中的位置0搜索来节省一些搜索时间,如果您知道它可以'例如,在40号角之前。

在分隔符字符上使用String Spilled函数分割一条线是很常见的,这会返回一个字符串数组。然后,您只能在值可能出现的字段内搜索。这也会更快。

答案 3 :(得分:1)

在提高性能时,您需要确保您想要改进的内容实际上是导致瓶颈的原因。我将首先分析我的代码中各个部分的性能,然后找出哪个部分引入了最大的瓶颈。

乍一看,我看到这个过程的三个主要组成部分值得分析:

  • 遍历文件的全部内容(这可能包括也可能不包括文件IO注意事项,您可能希望单独测试)。
  • 检查String.Contains()文件中的每一行。
  • 当有比赛时
  • “Do Stuff”

虽然有很棒的商业资讯制作者,你可以先在代码中使用一个简单的计时器(如System.Diagnostics.Stopwatch),或者,如果过程非常漫长,只需使用watch来测量时间。

测量以下各项的时间。

  1. 测量只需查看整个文件而不执行任何其他操作所需的时间。这会隔离您的循环和IO代码,以便您可以看到它的执行情况。
  2. 接下来,只添加字符串比较(String.Contains)并衡量添加此字符串所需的总时间。
  3. 最后,添加“do stuff”代码并使用此添加来衡量总时间。
  4. 现在,您可以比较每个组件的添加成本,以确定该组件的成本以及是否需要对其进行改进。例如,假设您录制了以下时间:

    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秒,我会看看旁边的代码,看看是否有任何我可以改进的地方。最后,我会看一下字符串比较。

    当然,我提供的时间测量值是虚构的,但您可以在应用程序中对性能分析应用相同的步骤。关键是确定最高成本并尝试减少首先

答案 4 :(得分:1)

令我惊讶的是,编译后的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语句混乱。但它很快,真的快。

答案 5 :(得分:0)

当且仅当此字符串包含指定的char值序列时,String.Contains方法才返回true。

它主要用于检查字符串中的子字符串。

http://csharp.net-informations.com/string/csharp-string-contains.htm

答案 6 :(得分:0)

Contains,就像所有字符串搜索方法(我认为)一样,最终会导致外部方法InternalFindNLSStringEx - 无论如何,它都是相当不可用的。

相关问题