最快的 C# 忽略大小写的字符串替换






4.72/5 (27投票s)
2005年7月4日
2分钟阅读

361307

1634
找到一种快速的方法来替换不区分大小写的字符串。
引言
在处理数据时,在许多情况下,我们需要处理字符串(字符串可以是任何数据的最终形式,甚至是二进制数据),其中,替换是经常使用的。我们可以发现,在.NET中进行字符串替换很容易,因为有不同的方法可以完成它,例如String.Replace
、System.Text.Regex
、迭代String.SubString
,甚至使用 Microsoft Visual Basic RunTime 的 (Microsoft.VisualBasic.DLL) Strings.Replace
函数。但是,它们都有缺点。因此,发现了一种新的方法来执行快速的、不区分大小写的字符串替换。
背景
我正在开发一个网络资源挖掘系统,我需要处理大量网站页面的字符串替换。由于字符可能因大小写而异,因此不区分大小写是核心要求,速度也非常重要。但我找不到令人满意的方法,作为独立的(纯 C#)、快速且不区分大小写的字符串替换函数。
使用代码
在 .NET 中,不使用 C++/CLI,有几种方法可以执行字符串替换
- 最常用的方法是
String.Replace
,但是不支持不区分大小写。 System.Text.Regex
(正则表达式)可以通过将RegExpOption
定义为IgnoreCase
来实现不区分大小写,但效率不高。- 对于少量的替换,迭代
String.SubString
并使用常规字符串连接 (+) 似乎比 Regex 更快。 - 与方法 3 唯一的区别是使用
StringBuilder
而不是“+”。为什么它更快?CodeProject 上有一些文章讨论了这个问题。 - 导入 Microsoft Visual Basic RunTime (Microsoft.VisualBasic.DLL) 命名空间并使用
Strings.Replace
函数,确实很快!但是,一些 C# 用户不喜欢这样。 - 嘿,为什么不反编译方法 5 呢?我使用了 Reflector 并借助 Denis Bauer 的 Reflector.FileDisassembler,反编译了
Strings.Replace
的源代码(它有一些相关函数,例如Strings.Split
和Strings.Join
等)。它和方法 5 一样快,但没有使用 Microsoft Visual Basic RunTime。
那么,有没有什么方法可以实现更好甚至更高的性能呢?这就是超快的字符串替换方法
private static string ReplaceEx(string original,
string pattern, string replacement)
{
int count, position0, position1;
count = position0 = position1 = 0;
string upperString = original.ToUpper();
string upperPattern = pattern.ToUpper();
int inc = (original.Length/pattern.Length) *
(replacement.Length-pattern.Length);
char [] chars = new char[original.Length + Math.Max(0, inc)];
while( (position1 = upperString.IndexOf(upperPattern,
position0)) != -1 )
{
for ( int i=position0 ; i < position1 ; ++i )
chars[count++] = original[i];
for ( int i=0 ; i < replacement.Length ; ++i )
chars[count++] = replacement[i];
position0 = position1+pattern.Length;
}
if ( position0 == 0 ) return original;
for ( int i=position0 ; i < original.Length ; ++i )
chars[count++] = original[i];
return new string(chars, 0, count);
}
现在让我们做一些比较:测试用例是首先生成一个长字符串,然后迭代替换 1000 次。我们包括String.Replace
,以便让我们了解区分大小写和不区分大小写选项之间的差异。请记住:String.Replace
不支持不区分大小写!
static void Main(string[] args)
{
string segment = "AaBbCc";
string source;
string pattern = "AbC";
string destination = "Some";
string result = "";
const long count = 1000;
StringBuilder pressure = new StringBuilder();
HiPerfTimer time;
for (int i = 0; i < count; i++)
{
pressure.Append(segment);
}
source = pressure.ToString();
GC.Collect();
//regexp
time = new HiPerfTimer();
time.Start();
for (int i = 0; i < count; i++)
{
result = Regex.Replace(source, pattern,
destination, RegexOptions.IgnoreCase);
}
time.Stop();
Console.WriteLine("regexp = " + time.Duration + "s");
GC.Collect();
//vb
time = new HiPerfTimer();
time.Start();
for (int i = 0; i < count; i++)
{
result = Strings.Replace(source, pattern,
destination, 1, -1, CompareMethod.Text);
}
time.Stop();
Console.WriteLine("vb = " + time.Duration + "s");
GC.Collect();
//vbReplace
time = new HiPerfTimer();
time.Start();
for (int i = 0; i < count; i++)
{
result = VBString.Replace(source, pattern,
destination, 1, -1, StringCompareMethod.Text);
}
time.Stop();
Console.WriteLine("vbReplace = " + time.Duration + "s");// + result);
GC.Collect();
// ReplaceEx
time = new HiPerfTimer();
time.Start();
for (int i = 0; i < count; i++)
{
result = Test.ReplaceEx(source, pattern, destination);
}
time.Stop();
Console.WriteLine("ReplaceEx = " + time.Duration + "s");
GC.Collect();
// Replace
time = new HiPerfTimer();
time.Start();
for (int i = 0; i < count; i++)
{
result = source.Replace(pattern.ToLower(), destination);
}
time.Stop();
Console.WriteLine("Replace = " + time.Duration + "s");
GC.Collect();
//sorry, two slow :(
/*//substring
time = new HiPerfTimer();
time.Start();
for (int i = 0; i < count; i++)
{
result = StringHelper.ReplaceText(source, pattern,
destination, StringHelper.CompareMethods.Text);
}
time.Stop();
Console.WriteLine("substring =" + time.Duration + ":");
GC.Collect();
//substring with stringbuilder
time = new HiPerfTimer();
time.Start();
for (int i = 0; i < count; i++)
{
result = StringHelper.ReplaceTextB(source, pattern,
destination, StringHelper.CompareMethods.Text);
}
time.Stop();
Console.WriteLine("substringB=" + time.Duration + ":");
GC.Collect();
*/
Console.ReadLine();
}
结果
1¡¢string segment = "abcaBc";
regexp = 3.75481827997692s
vb = 1.52745502570857s
vbReplace = 1.46234256029747s
ReplaceEx = 0.797071415501132s !!!Replace = 0.178327413120941s
// ReplaceEx > vbReplace > vb > regexp
2¡¢string segment = "abcaBcabC";
regexp = 5.30117431126023s
vb = 2.46258449048692s
vbReplace = 2.5018721653171s
ReplaceEx = 1.00662179131705s !!!
Replace = 0.233760994763301s
// ReplaceEx > vb > vbReplace > regexp
3¡¢string segment = "abcaBcabCAbc";
regexp = 7.00987862982586s
vb = 3.61050301085753s
vbReplace = 3.42324876485699s
ReplaceEx = 1.14969947297771s !!!
Replace = 0.277254511397398s
// ReplaceEx > vbReplace > vb > regexp
4¡¢string segment = "ABCabcAbCaBcAbcabCABCAbcaBC";
regexp = 13.5940090151123s
vb = 11.6806222578568s
vbReplace = 11.1757614445411s
ReplaceEx = 1.70264153684337s !!!(my god!)
Replace = 0.42236820601501s
// ReplaceEx > vbReplace > vb > regexp
好吧,ReplaceEx
函数真的是在所有条件下最快的吗?
5¡¢string segment = "AaBbCc";
regexp = 0.671307945562914s
vb = 0.32356849823092s
vbReplace = 0.316965703741677s !!!
ReplaceEx = 0.418256510254795s
Replace = 0.0453026851178013s
// vbReplace > vb > ReplaceEx > regexp
为什么?瓶颈是
string upperString = original.ToUpper();
string upperPattern = pattern.ToUpper();
当没有字符串需要替换时,时间浪费在string.ToUpper()
中。
关注点
我喜欢资源挖掘和聚类。我开发了一个 GUI 系统,支持使用 Quick BASIC 7.1 的窗口、鼠标、多媒体、真彩色屏幕!
历史
- 2005.7.2 - 首次发布。