在上一编 文章里分享了自定义实现一个高效的String Split方法,接下来同样分享一下String相关操作的函数Replace.通过反编译查看String的Replace方法是内置实现无法查看具体 实现源码,因此无法推断出String的Replace方法实现如何;不过出于好奇自己手动去实现一个对应的Replace函数,从测试情况来看其效率相对来说比String的Replace方法要好些.
测试描述
为了让测试更全面所以进行不同内容替换和处理的不同次数添加到测试中.
-
测试用的String数据
Cache-Control:public, max-age=0
Content-Encoding:gzip
Content-Length:9480
Content-Type:text/html; charset=utf-8
Date:Wed, 31 Oct 2012 14:17:06 GMT
Expires:Wed, 31 Oct 2012 14:17:05 GMT
Last-Modified:Wed, 31 Oct 2012 14:17:05 GMT
P3P:CP=NON DSP COR ADM CUR DEV TAI OUR IND NAV PRE STA
P3P:CP=NON DSP COR ADM CUR DEV TAI OUR IND NAV PRE STA
Server:Microsoft-IIS/7.5
Set-Cookie:smark=Branch=default&IsProject=1; domain=.codeplex.com; expires=Fri, 31-Oct-2042 14:17:06 GMT; path=/
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
X-AspNetMvc-Version:4.0
X-Powered-By:ASP.NET
-
测试相关方法
static void StringReplace(string value,string olddata,string newdata){ for (int i = 0; i < count; i++) { value.Replace(olddata, newdata); }}static void StringExtendReplace(string value, string olddata, string newdata){ for (int i = 0; i < count; i++) { StringExtend.Replace(value,olddata, newdata); }}
-
具体测试代码
static void Main(string[] args){ value.Replace("1", "010"); StringExtend.Replace(value, "1", "010"); Console.SetOut(new System.IO.StreamWriter("test.txt")); count = 1; TestReplace(value, "1", "010"); count = 10; TestReplace(value, "C", "010"); count = 100; TestReplace(value, "W", "010"); count = 1000; TestReplace(value, "A", "010"); count = 10000; TestReplace(value, "0", "010"); count = 100000; TestReplace(value, ":", "010"); count = 1; TestReplace(value, "ASP.NET", "ASPX"); count = 10; TestReplace(value, "2012", "2048"); count = 100; TestReplace(value, "Wed", "MVC"); count = 1000; TestReplace(value, "Content", "TESTOPQ"); count = 10000; TestReplace(value, "ASP.NET", "ASPX"); count = 100000; TestReplace(value, "OUR", "BBQ"); Console.Out.Flush(); Console.Read();}static void TestReplace(string value, string olddata, string newdata){ Console.WriteLine("=========[Count:{0}\t ({1} To {2})]===============", count, olddata, newdata); System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Reset(); sw.Start(); StringExtendReplace(value, olddata, newdata); sw.Stop(); Console.WriteLine("StringExtendReplace:\t{0}ms", sw.Elapsed.TotalMilliseconds); sw.Reset(); sw.Start(); StringReplace(value, olddata, newdata); sw.Stop(); Console.WriteLine("StringReplace:\t\t{0}ms", sw.Elapsed.TotalMilliseconds); Console.WriteLine("");}
-
测试结果
=========[Count:1(1 To 010)]===============
StringExtendReplace:0.1728ms
StringReplace:0.1654ms
=========[Count:10(C To 010)]===============
StringExtendReplace:0.0538ms
StringReplace:0.1002ms
=========[Count:100(W To 010)]===============
StringExtendReplace:0.4561ms
StringReplace:0.9015ms
=========[Count:1000(A To 010)]===============
StringExtendReplace:5.4919ms
StringReplace:11.0184ms
=========[Count:10000(0 To 010)]===============
StringExtendReplace:52.7016ms
StringReplace:96.3369ms
=========[Count:100000(: To 010)]===============
StringExtendReplace:633.8311ms
StringReplace:1025.9258ms
=========[Count:1(ASP.NET To ASPX)]===============
StringExtendReplace:0.0067ms
StringReplace:0.0084ms
=========[Count:10(2012 To 2048)]===============
StringExtendReplace:0.0424ms
StringReplace:0.0889ms
=========[Count:100(Wed To MVC)]===============
StringExtendReplace:0.4074ms
StringReplace:0.8477ms
=========[Count:1000(Content To TESTOPQ)]===============
StringExtendReplace:4.5297ms
StringReplace:7.6571ms
=========[Count:10000(ASP.NET To ASPX)]===============
StringExtendReplace:43.2199ms
StringReplace:82.9951ms
=========[Count:100000(OUR To BBQ)]===============
StringExtendReplace:442.286ms
StringReplace:841.7506ms
方法源代码
[ThreadStatic] static char[] mTempChars; protected static char[] GetTempData() { if (mTempChars == null) mTempChars = new char[1024 * 64]; return mTempChars; } public static string Replace(string value, string oldData, string newData) { char[] tmpchars = GetTempData(); int newpostion = 0; int oldpostion = 0; int length = value.Length; int oldlength = oldData.Length; int newlength = newData.Length; int index = 0; int copylength = 0; bool eq = false; while (index < value.Length) { eq = true; for (int k = 0; k < oldlength; k++) { if (value[index + k] != oldData[k]) { eq = false; break; } } if (eq) { copylength = index - oldpostion; value.CopyTo(oldpostion, tmpchars, newpostion, copylength); newpostion += copylength; index += oldlength; oldpostion = index; newData.CopyTo(0, tmpchars, newpostion, newlength); newpostion += newlength; } else { index++; } } if (oldpostion < length) { copylength = index - oldpostion; value.CopyTo(oldpostion, tmpchars, newpostion, copylength); newpostion += copylength; } return new string(tmpchars, 0, newpostion); }
总结
通 过[ThreadStatic]来对每个线程分配一组Char[],从而达到最少Char[]的开销.细心的朋友应该会发现每个线程分析的char[]大 小为64k,换句话说这个方法替换后超过64k则会异常,不过也可以分析需要设置一个最大值.如果有需要也可以给函数添加不区分大小写替换