C# NET中的链式字符串替换
给定:base64字符串 调用时:C# NET中的链式字符串替换,c#,regex,string,replace,stringbuilder,C#,Regex,String,Replace,Stringbuilder,给定:base64字符串 调用时:Foo 然后:Foo返回带有以下字符替换的输入字符串('A'=>'、'B'=>'-'、'C'=>'+'),并尽可能快地执行该操作 我比较了几种算法,以确定哪个版本的Foo更快。结果指向普通的字符串。Replace,这非常令人惊讶。我本以为正则表达式在编译时会受到初步的影响,但随后会突破并超越string.Replace,它会在每次调用Foo时创建三个字符串副本 我想看看是否有其他人可以证实这些发现,或者解释为什么获胜者的表现优于其他人 我使用这些算法运行了Foo
Foo
然后:Foo
返回带有以下字符替换的输入字符串('A'=>'、'B'=>'-'、'C'=>'+')
,并尽可能快地执行该操作
我比较了几种算法,以确定哪个版本的Foo
更快。结果指向普通的字符串。Replace
,这非常令人惊讶。我本以为正则表达式在编译时会受到初步的影响,但随后会突破并超越string.Replace
,它会在每次调用Foo
时创建三个字符串副本
我想看看是否有其他人可以证实这些发现,或者解释为什么获胜者的表现优于其他人
我使用这些算法运行了Foo
100k次,结果是TimeSpan
,在完成执行后,在调试构建中使用StopWatch
进行测量:
00:00:00.0500790 <=== string.Replace [1]
00:00:00.0699696 <=== StringBuilder.Append [2]
00:00:00.0988960 <=== StringBuilder.Replace [3]
00:00:00.7440135 <=== Regex [4]
[2] :
[3] :
[4] :
在我看来,正则表达式要复杂得多
Replace直接调用Win32,可能会执行基于指针的字符串操作,以防止碎片化等(很难确定-但在托管代码中不会这样做)。如果我启动ILSpy,我会看到RegEx.Replace执行大量边界检查,然后进行匹配,然后使用StringBuilder执行对您的代理的调用结果。在我看来,RegEx要复杂得多
Replace直接调用Win32,可能会执行基于指针的字符串操作,以防止碎片化等(很难确定-但在托管代码中不会这样做)。如果我启动ILSpy,我会看到RegEx.Replace进行了大量的边界检查,然后进行匹配,然后使用StringBuilder执行对您的委托的调用结果。如果我们检查您指定的方法的实现,我们将不会感到意外
包括模式匹配和大量字符串连接,这会导致开销。虽然普通的C++使用C++实现(
包括模式匹配和大量字符串连接,这会导致开销。而普通的C++直接实现(<代码> CONSCON.CPP文件),它是低层次的,而且最有可能是非常优化的。 < P>是的,我对你所发现的也感到惊讶…并不是说Regex是最慢的,这是意料之中的。。。但是链式的
字符串。Replace
与StringBuilder
一样执行。
我自己做了一些检查,比较了与您相同的[1],但我修改了[2],使其尽可能接近裸骨实现O(n)
[1]
[2]
静态字符串Foo2(字符串输入)
{
变量长度=输入长度;
var sb=新字符[长度];
for(int i=0;i
我的测试字符串长度超过700万个字符(7230872,Lorem Ipsum)。
因此,我们可以注意到以下几点:
Foo
和Foo2
之间的差异变得越来越明显。使用15个字符替换时,Foo
大约在240毫秒内执行,而Foo2
大约在60毫秒内执行所以。。。总之。。。这里没有魔法,它只是执行得非常快…:) 是的,你的发现也让我很惊讶。。。并不是说Regex是最慢的,这是意料之中的。。。但是链式的
字符串。Replace
与StringBuilder
一样执行。
我自己做了一些检查,比较了与您相同的[1],但我修改了[2],使其尽可能接近裸骨实现O(n)
[1]
[2]
静态字符串Foo2(字符串输入)
{
变量长度=输入长度;
var sb=新字符[长度];
for(int i=0;i
我的测试字符串长度超过700万个字符(7230872,Lorem Ipsum)。
因此,我们可以注意到以下几点:
Foo(string input)
{
return input.Replace("A", "_").Replace("B", "-").Replace("C", "+");
}
Foo(string input)
{
var sb = new StringBuilder(input.Length);
foreach (var x in input)
{
if (x == 'A')
{
sb.Append('_');
}
else if (x == 'B')
{
sb.Append('-');
}
else if (x == 'C')
{
sb.Append('+');
}
else
{
sb.Append(x);
}
}
return sb.ToString();
}
Foo(string input)
{
return new StringBuilder(input, input.Length).Replace("A", "_").Replace("B", "-").Replace("C", "+").ToString()
}
static readonly Regex charsRegex = new Regex(@"[ABC]", RegexOptions.Compiled);
Foo(string input)
{
charsRegex.Replace(input, delegate (Match m)
{
var value = m.Value;
if (value == "A")
{
return "_";
}
else if (value == "B")
{
return "-";
}
else if (value == "C")
{
return "+";
}
return value;
});
}
static string Foo(string input)
{
string result = input.Replace("A", "_");
result = result.Replace("B", "-");
result = result.Replace("C", "+");
return result;
}
static string Foo2(string input)
{
var length = input.Length;
var sb = new char[length];
for (int i = 0; i < length; i++)
{
switch (input[i])
{
case 'A':
sb[i] = '_';
break;
case 'B':
sb[i] = '-';
break;
case 'C':
sb[i] = '+';
break;
default:
sb[i] = input[i];
break;
}
}
return sb.ToString();
}
public /*unsafe*/ static string Foo(string text)
{
char[] a = text.ToCharArray();
for(int i = 0; i < a.Length; i++)
switch(a[i])
{
case 'A': a[i] = '_'; break;
case 'B': a[i] = '-'; break;
case 'C': a[i] = '+'; break;
}
return new string(a);
}
public /*unsafe*/ static string Foo(string text)
{
char[] a = new char[text.Length];
for(int i = 0; i < text.Length; i++)
{
char c=text[i];
switch(c)
{
case 'A': a[i] = '_'; break;
case 'B': a[i] = '-'; break;
case 'C': a[i] = '+'; break;
default: a[i] = c; break;
}
}
return new string(a);
}