对于一串字符串"abfeuiowqjiqopeuwqiopewq",检测其中是否包含特定字符串"oweu",我常用的方法如下:
if (row.IndexOf(pattern) > -1)
以前从来没有想过这样有什么问题,但最近需要处理较大数据量的字符串,需要从各个方面考虑如何提高处理的效率,其中“检测是否包含特定字符串”也是重要的一环。
在.Net 2.0之后,其实有更简洁的方法 row.Contains(pattern),直接返回布尔值。看起来这种方法更原生态一些,可能效率更高。
此外,从正则表达式的角度来想,检测是否包含特定字符串其实是正则表达式最简单的一种应用,所以利用 Regex.IsMatch(row,pattern) 也是一种选择。
事实上,继续细分,会有如下几种方式:
直接调用 row.Contains(pattern); 那么 Contains 方法到底是怎么实现的呢?这里有童鞋告诉我们 http://stackoverflow.com/questions/498686/net-is-string-contains-faster-than-string-indexof ,Contains 实际是调用了 IndexOf 方法。
public bool Contains(string value)
{
return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
}
注意,这里的 IndexOf 方法还包含了第二个参数 StringComparison.Ordinal,而不带参数的 IndexOf 方法,其实应用的是默认的 StringComparison.CurrentCulture 参数。
至于这两种参数在性能上有什么区别,我们可以猜测默认的参数由于和系统的 Culture 相关,所以可能会有额外的性能开销。
如在 Contains方法里讨论的,即使是同样调用 IndexOf 方法,也可能由于 StringComparison 参数的不同而有不同的效率,所以实际上 IndexOf 方法可以再细分为
row.IndexOf(pattern) > –1;
row.IndexOf(pattern,StringComparison.Ordinal) > –1;
使用正则表达式,也可以分为两种情况:
1) 直接调用 Regex 的静态方法。Regex.IsMatch(row,pattern);
2) 构造一个 Regex 类型的实例,然后用实例的方法来检测。这样的好处是在初始化的时候就设好正则规则,而不需要每次动态设置。
而构建一个 Regex 类型的实例也会有多种方法,最简单的当然是直接 var regex = new Regex(pattern); 也可以在构建时增加 RegexOptions.Compiled 参数,将正则规则编译到内存中,还可以更麻烦一点,利用 Regex.CompileToAssembly 方法,将正则规则编译到 Dll 里,然后引用 Dll,这样节省了每次编译的消耗。具体如何使用 Regex.CompileToAssembly 方法,请参考 http://topic.csdn.net/u/20100510/10/c140542a-5624-4c5c-8e52-587b50315d05.html
在做测试之前,我们可以先根据上述几种方法的特点做个预测:
下面就做一个性能测试。测试数据是一个1G的文件,大概有一百万行的数据,每行进行一次比较。每种方法运行十次,取后九次的平均运行时间(避免第一次有 .Net 冷启的差异)。共测试了8种方法:
测试结果如下:
可以看到,这里 Contains 方法和带 StringComparison.Ordinal 参数的方法以及非静态的 Regex方法效率类似,Regex 方法似乎性能略优,但很不明显;静态的 Regex 方法属于第二梯队,而直接调用无参的 IndexOf 方法效率垫底。基本符合我们之前的猜测。
此外,这里还有一篇研究 IndexOf 的性能的文章 http://www.dotnetperls.com/indexof。其中比较有意思的一点是,对于单独一个字母 a,使用 IndexOf('a') 的性能比 IndexOf("a") 的性能高很多。