在C#中优化字符串操作
性能测试代码:/Files/zhuqil/PerformanceTest.zip
程序员通常都希望自己能的编写易读,易维护和易扩展的代码。然而,某些情况下,性能变成最重要的事情。本文提供了几个有用的窍门,来提高你常见的字符串操作的性能。
最近,我花了很多时间去研究一个简单的代码编辑器。这个应用程序的主要特点之一是语法高亮,实现这样的功能,性能是关键。我花了很多时间来优化我的代码。在做这个项目的同时,我也学到了很多。在这里,我向大家分享一下我的经验。
附在本文中的项目包含一个简单的基准测试应用程序,来比较下面描述的方法。
N0.1:在字符串中搜索一个单词
对字符串进行搜索是一件常见的任务。有几种方法可以做到这一点,下面将讨论每种方法。
1、使用正则表达式
正则表达式是一个非常强大的,有用的,对于数据验证和字符串搜索是非常快速的 ,但是,当性能很重要,正则表达式可能成为一场噩梦,你可以做几件事,使你的代码运行得更快。
规则#1:写一个好的正则表达式
写一个正则表达式可能很容易,但写一个有效的正则表达式,也是一个挑战,优化正则表达式已经超出了本文的范围,有在网络上有许多讨论这一主题文章和书籍。最重要的规则是:保持简单,复杂的规则表达式,包括大量的交替,通常需要很长时间才能执行。
规则#2:不要使用Regex类的静态方法
Regex类提供了一些静态的方法,进行基本的操作。下面的代码查找字符串变量input 是否包含变量pattern。
string pattern = "fox";
if (Regex.IsMatch(input, pattern)) {
/* More code here... */
}
IsMatch方法根据pattern变量中创建一个Regex对象,然后试着匹配input字符串,这个过程表现地非常缓慢,正则表达式不重复使用,这个才非常有用。
规则#3:尽可能地重复使用Regex对象
如上所述,一个正则表达式对象的创建需要一段时间,你应该避免频繁创建。在某些情况下,您可以在应用程序的启动的时候初始化一切必要的正则表达式,然后多次利用他们来处理长段的文本。这将会提高性能。
规则#4:考虑使用编译的正则表达式
当创建一个正则表达式对象,如果你使用编译的选项,性能会更好。
虽然有几个缺点,编译正则表达式增加了应用程序的启动时间,造成更多的内存使用,Jeff Atwood 写了一篇文章,讨论了编译的正则表达式优势和缺点:
http://www.codinghorror.com/blog/archives/000228.html
2、使用indexOf()方法
在一个字符串内进行搜索的时候,字符串类型IndexOf方法是非常有用的。你应该知道一些东西,来有效使用此方法。
a、尽量使用char类型
有两个IndexOf方法重载方法,第一个参数可以是一个字符或一个字符串,使用字符重载方法将快很多。如果您知道您正在搜索字符串的长度为1,请使用char类型代替。
b、一个不成功搜索所花的时间将超过一个成功的搜索:
这其实并不奇怪,如果IndexOf方法成功,它将返回给定的字符串或字符首次出现的位置,它省去了剩余字符串的搜索时间。 相反,如果输入的字符串不包含给定的字符或者字符串,IndexOf函数将对整个输入字符串进行搜索。
c、经常有一种方法来消除不成功的搜索,例如,如果你想知道输入的字符串是否包含了一组单词的,你可以先搜索那些更可能出现在输入字符串的字符。
3、从零开始写的文本搜索的方法
现在,到了的最有趣的部分。IndexOf方法搜索一个字符时是相当快的,但搜索一个单词是缓慢的,如何有一个更快的方式在字符串内搜索单词呢?
花了一些时间,为寻找可能最快的的解决办法,以下方法是我想出的。
bool found;
int limit = source.Length - pattern.Length + 1;
if (limit < 1) return -1;
// Store the first 2 characters of "pattern"
char c0 = pattern[0];
char c1 = pattern.Length > 1 ? pattern[1] : ' ';
// Find the first occurrence of the first character
int first = source.IndexOf(c0, 0, limit);
while (first != -1) {
// Check if the following character is the same like the 2nd character of "pattern"
if (pattern.Length > 1 && source[first + 1] != c1) {
first = source.IndexOf(c0, ++first, limit - first);
continue;
}
// Check the rest of "pattern" (starting with the 3rd character)
found = true;
for (int j = 2; j < pattern.Length; j++)
if (source[first + j] != pattern[j]) {
found = false;
break;
}
// If the whole word was found, return its index, otherwise try again
if (found) return first;
first = source.IndexOf(c0, ++first, limit - first);
}
return -1;
}
此函数基于IndexOf方法搜索一个字符非常快这个事实的。
基本想法非常的简单,这个方法先搜索这个单词的第一个字符。如果一旦找到,它检测下一个的字符,该代码是有点乱,因为它包含了很多小的优化。例如,我发现,如果该方法在启动检测这个词的其余部分的“for”循环之前检查第二个字符(第一个已近被找到),平均性能将会更好。
我已经对随机生成的字符串进行大量测试,比较上述所列三种方案和这里是结果:
/* .... */
input.Replace("\\", "\\\\").Replace("{", "\\{").Replace("}", "\\}");
另一种简单的方法是使用的字符串类型Replace方法,当建立RTF文件,该更换程序将如下:这个易读,简短和干净的,但它并不总是最快的选择。
int i = source.IndexOfAny(escapeChars);
while (i != -1) {
source = source.Insert(i, escape.ToString());
i = source.IndexOfAny(escapeChars, i + 2);
}
return source.ToString();
}
第一个参数是输入字符串,第二个是一个被转义字符数组,最后一个参数是转义字符,正如你可能知道,如果你需要做连续许多操作,直接编辑字符串的效率不是很高。StringBuilder类提供了一种有效的方式来编辑字符串,虽然它需要一点时间来初始化一个StringBuilder对象,如果你知道你需要反复编辑字符串,这需要合理的使用StringBuilder。
StringBuilder s = new StringBuilder();
int j = 0;
int i = source.IndexOfAny(escapeChars);
while (i != -1) {
s.Append(source.Substring(j, i - j));
s.Append(escape);
j = i;
i = source.IndexOfAny(escapeChars, j + 1);
}
s.Append(source.Substring(j));
return s.ToString();
}
我使用了3个方法来取代转义序列的3字符集描述方法。下图显示,该方法是最快的一个取决于输入字符串:
Concatenating Strings Efficiently by Jon Skeet
StringBuilder vs String by Alois Kraus
(全文完)
以下为广告部分
您部署的HTTPS网站安全吗?
如果您想看下您的网站HTTPS部署的是否安全,花1分钟时间来 myssl.com 检测以下吧。让您的HTTPS网站变得更安全!
快速了解HTTPS网站安全情况。
安全评级(A+、A、A-...)、行业合规检测、证书信息查看、证书链信息以及补完、服务器套件信息、证书兼容性检测等。
安装部署SSL证书变得更方便。
SSL证书内容查看、SSL证书格式转换、CSR在线生成、SSL私钥加解密、CAA检测等。
让服务器远离SSL证书漏洞侵扰
TLS ROBOT漏洞检测、心血漏洞检测、FREAK Attack漏洞检测、SSL Poodle漏洞检测、CCS注入漏洞检测。