在大型文本模板中替换标记的最佳方法
Posted
技术标签:
【中文标题】在大型文本模板中替换标记的最佳方法【英文标题】:Best way to replace tokens in a large text template 【发布时间】:2010-09-06 10:09:30 【问题描述】:我有一个大型文本模板,需要用其他文本替换标记化部分。令牌看起来像这样:##USERNAME##。我的第一直觉就是使用 String.Replace(),但有没有更好、更有效的方法,或者 Replace() 是否已经为此进行了优化?
【问题讨论】:
【参考方案1】:System.Text.RegularExpressions.Regex.Replace() 是您所寻找的 - 如果您的令牌足够奇怪,您需要一个正则表达式来找到它们。
Some kind soul did some performance testing,在 Regex.Replace()、String.Replace() 和 StringBuilder.Replace() 之间,实际上 String.Replace() 排在首位。
【讨论】:
我相信他们在不适用于 C# 的 PowerShell 中进行了测试。 C3 的内存管理与 PowerShell 不同,并且不会将 StringBuilder 转换为 String 以替换其中的字符。另一方面,RegEx 和 StringBuilder 在大数据量上效果更好,这些数据以块的形式添加到它们中 RegEx 是对大量文本执行替换的可怕选项。尽管它很强大,但许多 RegEx 支持者将整个世界视为钉子,将 RegEx 视为他们的锤子。对于涉及大量文本的用例,请查看 FastReplacer ***.com/a/11442008/141172【参考方案2】:我必须这样做的唯一情况是发送模板电子邮件。在 .NET 中,这是由 MailDefinition class 提供的。这就是您创建模板消息的方式:
MailDefinition md = new MailDefinition();
md.BodyFileName = pathToTemplate;
md.From = "test@somedomain.com";
ListDictionary replacements = new ListDictionary();
replacements.Add("<%To%>", someValue);
// continue adding replacements
MailMessage msg = md.CreateMailMessage("test@someotherdomain.com", replacements, this);
在此之后,将通过替换模板中的值来创建 msg.Body。我想你可以看看 MailDefinition.CreateMailMessage() with Reflector :)。抱歉有点跑题了,但如果这是你的场景,我认为这是最简单的方法。
【讨论】:
【参考方案3】:嗯,取决于您的模板中有多少变量,您有多少模板等等。这可能是一个完整的模板处理器的工作。我曾经为 .NET 使用过的唯一一个是 NVelocity,但我相信肯定有很多其他的,其中大多数都链接到某个 Web 框架或另一个。
【讨论】:
【参考方案4】:string.Replace 很好。我更喜欢使用正则表达式,但我是 *** 正则表达式。
要记住的是这些模板有多大。如果它真的很大,并且内存是一个问题,您可能想要创建一个作用于流的自定义标记器。这样,您在操作文件时只将文件的一小部分保存在内存中。
但是,对于简单的实现,string.Replace 应该没问题。
【讨论】:
【参考方案5】:如果您要对大字符串进行多次替换,那么使用 StringBuilder.Replace() 可能会更好,因为字符串通常会出现性能问题。
【讨论】:
【参考方案6】:正则表达式将是编码的最快解决方案,但如果您有许多不同的标记,那么它会变慢。如果性能不是问题,请使用此选项。
更好的方法是定义标记,例如您可以在文本中扫描的“##”。然后从哈希表中选择要替换的内容,以标记后面的文本作为键。
如果这是构建脚本的一部分,那么 nAnt 有一个很棒的功能可以做到这一点,称为Filter Chains。其代码是开源的,因此您可以查看它是如何实现快速实现的。
【讨论】:
【参考方案7】:最近不得不做类似的事情。我所做的是:
制作一个采用字典的方法(键 = 令牌名称,值 = 您需要插入的文本) 使用 Regex.Matches(input, regular expression) 获取与您的标记格式匹配的所有匹配项(我猜你的情况是##.+?##,不是很擅长正则表达式 :P) 遍历结果,使用字典查找令牌的插入值。 返回结果。完成;-)
如果你想测试你的正则表达式,我可以建议the regulator.
【讨论】:
【参考方案8】:FastReplacer 在 O(n*log(n) + m) 时间内实现了令牌替换,并使用了原始字符串 3 倍的内存。
FastReplacer 适用于在性能很重要时对大字符串执行许多替换操作。
主要思想是避免在每次替换字符串时修改现有文本或分配新内存。
我们设计了 FastReplacer 来帮助我们完成一个项目,在该项目中,我们必须生成包含大量追加和替换操作的大文本。应用程序的第一个版本使用 StringBuilder 生成文本需要 20 秒。使用 String 类的第二个改进版本耗时 10 秒。然后我们实现了 FastReplacer,持续时间降到了 0.1 秒。
【讨论】:
这就是我要找的。请查看速度表并试一试。【参考方案9】:如果您的模板很大并且有很多标记,您可能不想遍历它并一个一个替换模板中的标记,因为这会导致 O(N * M) 操作,其中 N 是模板的大小,M 是要替换的标记数。
以下方法接受模板和您希望替换的键值对的字典。通过将 StringBuilder 初始化为略大于模板的大小,它应该会导致 O(N) 操作(即它不应该自己增长 log N 倍)。
最后,您可以将令牌的构建移到 Singleton 中,因为它只需要生成一次。
static string SimpleTemplate(string template, Dictionary<string, string> replacements)
// parse the message into an array of tokens
Regex regex = new Regex("(##[^#]+##)");
string[] tokens = regex.Split(template);
// the new message from the tokens
var sb = new StringBuilder((int)((double)template.Length * 1.1));
foreach (string token in tokens)
sb.Append(replacements.ContainsKey(token) ? replacements[token] : token);
return sb.ToString();
【讨论】:
【参考方案10】:这是正则表达式的理想用法。查看this helpful website、.Net Regular Expressions class 和这本非常有用的书Mastering Regular Expressions。
【讨论】:
以上是关于在大型文本模板中替换标记的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章