65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.72/5 (27投票s)

2005年7月4日

2分钟阅读

viewsIcon

361307

downloadIcon

1634

找到一种快速的方法来替换不区分大小写的字符串。

引言

在处理数据时,在许多情况下,我们需要处理字符串(字符串可以是任何数据的最终形式,甚至是二进制数据),其中,替换是经常使用的。我们可以发现,在.NET中进行字符串替换很容易,因为有不同的方法可以完成它,例如String.ReplaceSystem.Text.Regex、迭代String.SubString,甚至使用 Microsoft Visual Basic RunTime 的 (Microsoft.VisualBasic.DLL) Strings.Replace 函数。但是,它们都有缺点。因此,发现了一种新的方法来执行快速的、不区分大小写的字符串替换。

背景

我正在开发一个网络资源挖掘系统,我需要处理大量网站页面的字符串替换。由于字符可能因大小写而异,因此不区分大小写是核心要求,速度也非常重要。但我找不到令人满意的方法,作为独立的(纯 C#)快速不区分大小写的字符串替换函数。

使用代码

在 .NET 中,不使用 C++/CLI,有几种方法可以执行字符串替换

  1. 最常用的方法是String.Replace,但是不支持不区分大小写。
  2. System.Text.Regex(正则表达式)可以通过将RegExpOption定义为IgnoreCase来实现不区分大小写,但效率不高。
  3. 对于少量的替换,迭代String.SubString并使用常规字符串连接 (+) 似乎比 Regex 更快。
  4. 与方法 3 唯一的区别是使用StringBuilder而不是“+”。为什么它更快?CodeProject 上有一些文章讨论了这个问题。
  5. 导入 Microsoft Visual Basic RunTime (Microsoft.VisualBasic.DLL) 命名空间并使用Strings.Replace函数,确实很快!但是,一些 C# 用户不喜欢这样。
  6. 嘿,为什么不反编译方法 5 呢?我使用了 Reflector 并借助 Denis Bauer 的 Reflector.FileDisassembler,反编译了 Strings.Replace 的源代码(它有一些相关函数,例如Strings.SplitStrings.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 - 首次发布。
© . All rights reserved.