Replace方法与正则表达式的性能比较

今天做项目时遇到一个小需求:要将字符串中的回车符号替换成其它符号(比如"<br/>")。 考虑到不同的情况下,有些系统中是用\r\n作回车符,有些仅用\n就代表回车符了。以前都是用String类的Replace方法连接替换多次来处理的,今天突然想改为正则表达式一次性搞定,但又怕性能上消耗太大,于是写了下面的测试代码:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string a = "11111 \n 22222 \r 33333 \n\r 44444 \r\n 55555 \r 66666";

            Console.Write(Replace(a) + "\n");
            Console.Write(RegReplace(a, "(\r|\n)+", "*") + "\n\n");

            Stopwatch sw = new Stopwatch();

            int count = 50000;
            int times = 5;
            long result = 0;

            for (int i = 0; i < times; i++)
            {
                result += Test(sw, count, a, false);
            }

            Console.Write("\n{0}次×{1}轮测试,[Replace]方法平均每轮速度:{2}\n", count, times, result / times);
            Console.Write("\n");

            result = 0;
            for (int i = 0; i < times; i++)
            {
                result += Test(sw, count, a, true);
            }

            Console.Write("\n{0}次×{1}轮测试,[正则表达式]方法平均每轮速度:{2}\n", count, times, result / times);
            Console.Read();
        }

        static string RegReplace(string strSrc, string strRegPattern, string strReplace)
        {
            return System.Text.RegularExpressions.Regex.Replace(strSrc, strRegPattern, strReplace);
        }

        static string Replace(string strSrc)
        {
            return strSrc.Replace("\r\n", "*").Replace("\n\r", "*").Replace("\n", "*").Replace("\r", "*");
        }

        static long Test(Stopwatch sw, int count, string a, bool useRegularExpressions)
        {
            int i = 0;
            sw.Reset();
            sw.Start();
            for (i = 0; i < count; i++)
            {
                if (useRegularExpressions)
                {
                    RegReplace(a, "(\r|\n)+", "*");
                }
                else
                {
                    Replace(a);
                }
            }
            sw.Stop();
            Console.Write(sw.ElapsedMilliseconds + "\n");
            return sw.ElapsedMilliseconds;
        }
    }
}

输出结果:

11111 * 22222 * 33333 * 44444 * 55555 * 66666 11111 * 22222 * 33333 * 44444 * 55555 * 66666

94 89 88 86 83

50000次×5轮测试,[Replace]方法平均每轮速度:88

333 327 321 327 332

50000次×5轮测试,[正则表达式]方法平均每轮速度:328

可以看出,正则表达式要慢一倍都不止,大概慢 328/88 =3.7倍 (当然改变字符串的长度以及回车符的数量与位置,结果又会有一些差异)

注:经 Edwin Liu 在回复中提醒,正则表达式编译预热后速度要快一点,今天把测试代码改了下

using System;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Regex reg = new Regex("(\r|\n)+", RegexOptions.Compiled);
            string a = "11111 \n 22222 \r 33333 \n\r 44444 \r\n 55555 \r 66666";

            Console.Write(Replace(a) + "\n");
            Console.Write(reg.Replace(a, "*") + "\n\n");

            Stopwatch sw = new Stopwatch();

            int count = 50000;
            int times = 5;
            long result = 0;

            for (int i = 0; i < times; i++)
            {
                result += Test(sw, count, a, false);
            }

            Console.Write("\n{0}次×{1}轮测试,[Replace]方法平均每轮速度:{2}\n", count, times, result / times);
            Console.Write("\n");

            result = 0;
            for (int i = 0; i < times; i++)
            {
                result += Test(sw, count, a, true);
            }
            Console.Write("\n{0}次×{1}轮测试,[正则表达式]方法平均每轮速度:{2}\n", count, times, result / times);
            Console.Read();
        }
        
        static string Replace(string strSrc)
        {
            return strSrc.Replace("\r\n", "*").Replace("\n\r", "*").Replace("\n", "*").Replace("\r", "*");
        }
        
        static long Test(Stopwatch sw, int count, string a, bool useRegularExpressions)
        {
            int i = 0;
            Regex reg = new Regex("(\r|\n)+", RegexOptions.Compiled);

            sw.Reset();
            sw.Start();
            for (i = 0; i < count; i++)
            {
                if (useRegularExpressions)
                {                   
                    reg.Replace(a, "*");
                }
                else
                {
                    Replace(a);
                }
            }
            sw.Stop();
            Console.Write(sw.ElapsedMilliseconds + "\n");
            return sw.ElapsedMilliseconds;
        }
    }
}

新的测试结果:

11111 * 22222 * 33333 * 44444 * 55555 * 66666 11111 * 22222 * 33333 * 44444 * 55555 * 66666

100 93 86 86 84

50000次×5轮测试,[Replace]方法平均每轮速度:89

204 200 201 210 190

50000次×5轮测试,[正则表达式]方法平均每轮速度:201

粗略比较一下:编译预热后 慢201/89=2.3倍,相当刚才的3.7倍确实有所提高,但是相对于String类的Replace方法仍然可以认为很慢。(不过从方便程度上讲,有些复杂的功能用正则表达式还是很方便的)

二、Silverlight 4.0环境

注:Silverlight中没有Stopwatch类,所以用DateTime.Now计时代替了,但这样可能结果不太精确;另外silverlight中的正则表达式也没有编译预热功能,所以只能用最原始的方法。

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Text.RegularExpressions;

namespace SilverlightApplication1
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            string a = "11111 \n 22222 \r 33333 \n\r 44444 \r\n 55555 \r 66666";

            Debug.WriteLine(Replace(a));
            Debug.WriteLine(RegReplace(a, "(\r|\n)+", "*") + "\n");

            int count = 50000;
            int times = 5;
            double result = 0;

            for (int i = 0; i < times; i++)
            {
                result += Test(count, a, false);
            }

            Debug.WriteLine("{0}次×{1}轮测试,[Replace]方法平均每轮速度:{2}\n", count, times, result / times);


            result = 0;
            for (int i = 0; i < times; i++)
            {
                result += Test(count, a, true);
            }

            Debug.WriteLine("{0}次×{1}轮测试,[正则表达式]方法平均每轮速度:{2}\n", count, times, result / times);
        }

       
        string RegReplace(string strSrc, string strRegPattern, string strReplace)
        {           
            return System.Text.RegularExpressions.Regex.Replace(strSrc, strRegPattern, strReplace);
        }
       
        string Replace(string strSrc)
        {
            return strSrc.Replace("\r\n", "*").Replace("\n\r", "*").Replace("\n", "*").Replace("\r", "*");
        }
       
        double Test(int count, string a, bool useRegularExpressions)
        {
            int i = 0;
            DateTime _start = DateTime.Now;
            for (i = 0; i < count; i++)
            {
                if (useRegularExpressions)
                {
                    RegReplace(a, "(\r|\n)+", "*");
                }
                else
                {
                    Replace(a);
                }
            }
            DateTime _end = DateTime.Now;

            TimeSpan span = _end - _start;

            Debug.WriteLine(span.TotalMilliseconds);
            return span.TotalMilliseconds;
        }
    }
}

输出结果:

11111 * 22222 * 33333 * 44444 * 55555 * 66666 11111 * 22222 * 33333 * 44444 * 55555 * 66666

78.0002 93.6001 93.6002 78.0001 93.6002 50000次×5轮测试,[Replace]方法平均每轮速度:87.36016

405.6007 405.6007 483.6009 405.6007 405.6007 50000次×5轮测试,[正则表达式]方法平均每轮速度:421.20074

可以看出,基本上跟Console程序在一个数量级(因为底层的CLR基本上是差不多的,这也符合预期,但貌似Silverlight的正则表达式要慢一点,估计跟没有编译预热功能有很大关系)

三、AS3.0的测试

注:前几天看到园子里有高手说AS3.0的性能大约是Silverlight的80%,很是好奇,所以最后也顺便放到AS3.0中测试了一下,但要注意的是:因为ActionScript3.0中String的replace方法跟JS一样,默认只能替换第一次找到的字符串,所以基本上要实现全盘替换,只能用正则表达式

import flash.utils.Timer;

function Replace(strSrc:String):String {
	var myPattern:RegExp = /\r|\n/gi; 
	return strSrc.replace(myPattern,"*");
}

function Test(strSrc:String,count:uint):int {
	var i:uint=0;
	var _start=getTimer();
	for (i=0; i<count; i++) {
		Replace(strSrc);
	}
	var _end=getTimer();
	var elapsedTime=_end-_start;
	trace(elapsedTime);
	return elapsedTime;
}

var a:String="11111 \n 22222 \r 33333 \n\r 44444 \r\n 55555 \r 66666";

trace(Replace(a));

var count:uint=50000;
var times:uint=5;
var rlt:Number=0;

for (var i:uint=0; i<times; i++) {
	rlt+=Test(a,count);
}

trace(count,"次×",times,"轮测试,[Replace]方法平均每轮速度:",rlt/times);

输出结果:

11111 * 22222 * 33333 ** 44444 ** 55555 * 66666 1547 1508 1509 1515 1504 50000 次× 5 轮测试,[Replace]方法平均每轮速度: 1516.6

但这个结果就很夸张了。

注:今天早上又测试了好几把,好象结果又快了一点,估计是昨天测试的时候有些程序没关

11111 * 22222 * 33333 ** 44444 ** 55555 * 66666 1048 1002 1001 1000 1009 50000 次× 5 轮测试,[Replace]方法平均每轮速度: 1012 当然上面的示例中,加了gi标志,即全局查找,并忽略大小写,如果去掉大小写标记,即var myPattern:RegExp = /\r|\n/gi; 改成 var myPattern:RegExp = /\r|\n/g; 速度能快一点

11111 * 22222 * 33333 ** 44444 ** 55555 * 66666 1015 971 972 970 971 50000 次× 5 轮测试,[Replace]方法平均每轮速度: 979.8

后记:本文的测试很是粗放,主要也就是看个大概,以便心中有个印象而已,欢迎高手做更精确的测试。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏GopherCoder

『Golang 内置模块库 template 』

59240
来自专栏hbbliyong

Extjs4.2+webAPI+EF实现分页以及webapi的数据传值

由于不明白分页的总数是怎么计算,不知道他的分页方式所以花费了好多功夫,现在弄出来了与大家分享下 1.首先是EF的简历,想必大家都清楚:添加-〉新建项-〉数据-〉...

33240
来自专栏程序猿

ASS II 码对照表

ASCII(American Standard Code for Information Interchange)定义从 0 到 127 的共128个数字所代表...

554140
来自专栏木宛城主

利用 PowerShell 分析SharePoint WebApplication 体系结构

之前一篇文章《两张图看清SharePoint 2013 Farm 逻辑体系结构》谈到Web Application,Content Database,Site...

21980
来自专栏大内老A

我的WCF之旅(10):如何在WCF进行Exception Handling

在任何Application的开发中,对不可预知的异常进行troubleshooting时,异常处理显得尤为重要。对于一般的.NET系统来说,我们简单地借助tr...

19680
来自专栏码农阿宇

JustMock .NET单元测试利器(三)用JustMock测试你的应用程序

用JustMock测试你的应用程序 本主题将指导您通过几个简单的步骤来使用Telerik®JustMock轻松测试您的应用程序。您将理解一个简单的原理,称为Ar...

38470
来自专栏林德熙的博客

C# Span 入门 stackallocAllocHGlobal

本文简单告诉大家如何使用 Span 新的功能 需要知道 Span 是 7.2 才有的功能,如果在正式项目使用,建议安装 Nuget 的方式

24920
来自专栏更流畅、简洁的软件开发方式

好大一棵树,新春的祝福(二):功能节点的数据结构和页面展示

1、数据结构      在原有的基础上,把noteID改成FunctionID,去掉code字段,增加三个字段。 NoteLevel :表示第几级的节点,可...

23050
来自专栏跟着阿笨一起玩NET

Winfrom 如何安全简单的跨线程更新控件

来源:http://www.cnblogs.com/rainbowzc/archive/2010/09/29/1838788.html

8610
来自专栏DOTNET

设计模式(行为模式)——观察者模式

1 定义 定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 2 适用性 1)当一个抽象模型有两个方面,其中一...

35460

扫码关注云+社区

领取腾讯云代金券