• 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

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

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    常用正则表达式(数字、字符、固定格式)
    阿里云serverless使用笔记
    http的get请求与post请求区别
    HTTPS各版本安全策略HTTPS1_1_TLS1_0、HTTPS2_TLS1_0、HTTPS2_TLS1_2
    REST式的web服务
    JAVA Remote Object
    AJAX的核心XMLHttpRequest对象
    SQL中TOP,LIMIT,ROWNUM的用法
    半双工模式和全双工模式
    win7系统网卡驱动正常,网线连接设备正常,但电脑右下角网络图片显示一直在转圈或者显示一个黄色感叹号的解决办法
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1764587.html
Copyright © 2020-2023  润新知