我有一个字符串,我需要用字典中的值替换标记。它必须尽可能高效。使用string.replace执行循环只会消耗内存(字符串是不可变的,请记住)。 StringBuilder.Replace()会更好,因为这是为了使用字符串操作吗?
我希望避免花费RegEx,但如果这样做会更有效,那么就这样吧。
注意:我不关心代码复杂性,只关心它运行的速度和消耗的内存。
平均统计数据:长度为255-1024个字符,字典中为15-30个字符。
答案 0 :(得分:67)
使用以下代码使用RedGate Profiler
class Program
{
static string data = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
static Dictionary<string, string> values;
static void Main(string[] args)
{
Console.WriteLine("Data length: " + data.Length);
values = new Dictionary<string, string>()
{
{ "ab", "aa" },
{ "jk", "jj" },
{ "lm", "ll" },
{ "yz", "zz" },
{ "ef", "ff" },
{ "st", "uu" },
{ "op", "pp" },
{ "x", "y" }
};
StringReplace(data);
StringBuilderReplace1(data);
StringBuilderReplace2(new StringBuilder(data, data.Length * 2));
Console.ReadKey();
}
private static void StringReplace(string data)
{
foreach(string k in values.Keys)
{
data = data.Replace(k, values[k]);
}
}
private static void StringBuilderReplace1(string data)
{
StringBuilder sb = new StringBuilder(data, data.Length * 2);
foreach (string k in values.Keys)
{
sb.Replace(k, values[k]);
}
}
private static void StringBuilderReplace2(StringBuilder data)
{
foreach (string k in values.Keys)
{
data.Replace(k, values[k]);
}
}
}
字符串长度= 1456
stringbuilder#1在方法中创建了stringbuilder,而#2则没有,所以性能差异最终会变得相同,因为你只是将该工作移出方法。如果你从stringbuilder而不是字符串开始,那么#2可能是你要去的方式。
就内存而言,使用RedGateMemory profiler,除非你进入很多整体上会赢得stringbuilder的替换操作,否则没有什么可担心的。
答案 1 :(得分:9)
这可能会有所帮助:
简短的回答似乎是String.Replace更快,尽管它可能会对你的内存占用/垃圾收集开销产生更大的影响。
答案 2 :(得分:6)
是的,StringBuilder
会让你获得速度和内存(主要是因为每次你用它操作时它都不会创建一个字符串实例 - StringBuilder
总是在同一个对象)。这是一个MSDN link,其中包含一些细节。
答案 3 :(得分:5)
stringbuilder.replace会比String.Replace更好吗
是的,好多了。如果你可以估计新字符串的上限(看起来你可以),那么它可能足够快。
当您创建它时:
var sb = new StringBuilder(inputString, pessimisticEstimate);
然后StringBuilder将不必重新分配其缓冲区。
答案 4 :(得分:1)
将数据从String转换为StringBuilder并返回将需要一些时间。如果只执行单个替换操作,则此时间可能无法通过StringBuilder中固有的效率改进来补偿。另一方面,如果将字符串转换为StringBuilder,然后对其执行许多Replace操作,并在最后将其转换回来,则StringBuilder方法会更快。
答案 5 :(得分:1)
不是在整个字符串上运行15-30次替换操作,而是使用类似trie数据结构的东西来保存字典可能更有效。然后,您可以遍历输入字符串一次,以进行所有搜索/替换。
答案 6 :(得分:1)
很大程度上取决于给定字符串中平均存在多少个标记。
在StringBuilder和String之间搜索键的性能可能类似,但如果必须在单个字符串中替换多个标记,StringBuilder将会获胜。
如果你只希望每个字符串平均有一个或两个标记,并且你的字典很小,我会选择String.Replace。
如果有许多标记,您可能需要定义自定义语法来识别标记 - 例如用括号中适当的转义规则封装在括号中。然后,您可以实现一个解析算法,该算法迭代字符串的字符一次,识别并替换它找到的每个标记。或者使用正则表达式。
答案 7 :(得分:1)
我的两分钱,我只是编写了几行代码来测试每种方法的执行方式,并且正如预期的那样,结果是&#34;它取决于&#34;。
对于较长的字符串,Regex
似乎表现得更好,对于较短的字符串,String.Replace
就是这样。我可以看到StringBuilder.Replace
的用法不是很有用,如果使用错误,它可能在GC透视图中是致命的(我试图共享StringBuilder
的一个实例。
答案 8 :(得分:1)
@ DustinDavis的问题&#39;答案是它以递归方式运行在同一个字符串上。除非您计划进行前后类型的操作,否则在这种测试中,每个操作案例都应该有单独的对象。
我决定创建自己的测试,因为我在整个网络上发现了一些相互矛盾的答案,我想完全确定。我正在处理的程序涉及大量文本(在某些情况下有数万行的文件)。
所以这是一个快速的方法,你可以复制和粘贴,看看自己哪个更快。您可能需要创建自己的文本文件进行测试,但您可以轻松地从任何地方复制和粘贴文本,并为自己制作足够大的文件:
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows;
void StringReplace_vs_StringBuilderReplace( string file, string word1, string word2 )
{
using( FileStream fileStream = new FileStream( file, FileMode.Open, FileAccess.Read ) )
using( StreamReader streamReader = new StreamReader( fileStream, Encoding.UTF8 ) )
{
string text = streamReader.ReadToEnd(),
@string = text;
StringBuilder @StringBuilder = new StringBuilder( text );
int iterations = 10000;
Stopwatch watch1 = new Stopwatch.StartNew();
for( int i = 0; i < iterations; i++ )
if( i % 2 == 0 ) @string = @string.Replace( word1, word2 );
else @string = @string.Replace( word2, word1 );
watch1.Stop();
double stringMilliseconds = watch1.ElapsedMilliseconds;
Stopwatch watch2 = new Stopwatch.StartNew();
for( int i = 0; i < iterations; i++ )
if( i % 2 == 0 ) @StringBuilder = @StringBuilder .Replace( word1, word2 );
else @StringBuilder = @StringBuilder .Replace( word2, word1 );
watch2.Stop();
double StringBuilderMilliseconds = watch1.ElapsedMilliseconds;
MessageBox.Show( string.Format( "string.Replace: {0}\nStringBuilder.Replace: {1}",
stringMilliseconds, StringBuilderMilliseconds ) );
}
}
我得到了那个字符串。每次交换8到10个字母单词时,它的速度提高了大约20%。如果您需要自己的经验证据,请亲自尝试。