今天做項目時遇到一個小需求:要将字元串中的回車符号替換成其它符号(比如"<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
94
89
88
86
83
50000次×5輪測試,[Replace]方法平均每輪速度:88
333
327
321
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;
}
}
}
新的測試結果:
100
93
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;
}
}
}
78.0002
93.6001
93.6002
78.0001
50000次×5輪測試,[Replace]方法平均每輪速度:87.36016
405.6007
483.6009
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
但這個結果就很誇張了。
注:今天早上又測試了好幾把,好象結果又快了一點,估計是昨天測試的時候有些程式沒關
1048
1002
1001
1000
1009
50000 次× 5 輪測試,[Replace]方法平均每輪速度: 1012
當然上面的示例中,加了gi标志,即全局查找,并忽略大小寫,如果去掉大小寫标記,即var myPattern:RegExp = /\r|\n/gi; 改成 var myPattern:RegExp = /\r|\n/g; 速度能快一點
1015
971
972
970
50000 次× 5 輪測試,[Replace]方法平均每輪速度: 979.8
後記:本文的測試很是粗放,主要也就是看個大概,以便心中有個印象而已,歡迎高手做更精确的測試。
作者:菩提樹下的楊過