如何使用StringBuilder进行多个不区分大小写的替换

我有一个(大)模板,想要替换多个值。 替换需要不区分大小写。 还必须能够拥有模板中不存在的密钥。

例如:

[TestMethod] public void ReplaceMultipleWithIgnoreCaseText() { const string template = "My name is @Name@ and I like to read about @SUBJECT@ on @website@, tag @subject@"; const string expected = "My name is Alex and I like to read about C# on stackoverflow.com, tag C#"; var replaceParameters = new List<KeyValuePair> { new KeyValuePair("@name@","Alex"), new KeyValuePair("@subject@","C#"), new KeyValuePair("@website@","stackoverflow.com"), // Note: The next key does not exist in template new KeyValuePair("@country@","The Netherlands"), }; var actual = ReplaceMultiple(template, replaceParameters); Assert.AreEqual(expected, actual); } public string ReplaceMultiple( string template, IEnumerable<KeyValuePair> replaceParameters) { throw new NotImplementedException( "Implementation needed for many parameters and long text."); } 

请注意,如果我有30个参数和一个大模板的列表,我不希望内存中有30个大字符串。 使用StringBuilder似乎是一种选择,但也欢迎使用其他解决方案。

解决方案我尝试但没有工作

这里找到的解决方案( C#String替换为字典 )在密钥不在收集中时抛出exception,但是我们的用户会犯错误,在这种情况下我想在文本中保留wromg密钥。 例:

 static readonly Regex re = new Regex(@"\$(\w+)\$", RegexOptions.Compiled); static void Main2() { // "Name" is accidentally typed by a user as "nam". string input = @"Dear $nam$, as of $date$ your balance is $amount$"; var args = new Dictionary( StringComparer.OrdinalIgnoreCase) { {"name", "Mr Smith"}, {"date", "05 Aug 2009"}, {"amount", "GBP200"}}; // Works, but not case insensitive and // uses a lot of memory when using a large template // ReplaceWithDictionary many args string output1 = input; foreach (var arg in args) { output1 = output1.Replace("$" + arg.Key +"$", arg.Value); } // Throws a KeyNotFoundException + Only works when data is tokenized string output2 = re.Replace(input, match => args[match.Groups[1].Value]); } 

这是基于Marc的答案,唯一真正的变化是替换期间的检查和边界正则表达式规则:

 static readonly Regex re = new Regex(@"\b(\w+)\b", RegexOptions.Compiled); static void Main(string[] args) { string input = @"Dear Name, as of dAte your balance is amounT!"; var replacements = new Dictionary(StringComparer.OrdinalIgnoreCase) { {"name", "Mr Smith"}, {"date", "05 Aug 2009"}, {"amount", "GBP200"} }; string output = re.Replace(input, match => replacements.ContainsKey(match.Groups[1].Value) ? replacements[match.Groups[1].Value] : match.Groups[1].Value); } 

这里是5000次迭代测试基准测试,没有看过内存或其他任何东西。

测试

替换function是您已检查为已接受的答案的function。

使用StringBuilder似乎是一种选择,但也欢迎使用其他解决方案。

既然你想要不区分大小写,我建议(非StringBuilder):

 public static string ReplaceMultiple( string template, IEnumerable> replaceParameters) { var result = template; foreach(var replace in replaceParameters) { var templateSplit = Regex.Split(result, replace.Key, RegexOptions.IgnoreCase); result = string.Join(replace.Value, templateSplit); } return result; } 

DotNetFiddle示例

我想我可能会尝试一些东西。 我使用类似于电子邮件模板的东西

  public string replace() { string appPath = Request.PhysicalApplicationPath; StreamReader sr = new StreamReader(appPath + "EmailTemplates/NewMember.txt"); string template = sr.ReadToEnd(); template = template.Replace("<%Client_Name%>", first_name.Text + " " + middle_initial.Text + " " + last_name.Text); //Add Customer data template = template.Replace("<%Client_First_Name%>", first_name.Text); template = template.Replace("<%Client_MI%>", middle_initial.Text); template = template.Replace("<%Client_Last_Name%>", last_name.Text); template = template.Replace("<%Client_DOB%>", dob.Text); return template; } 

在模板内部,您可以将<%%>等标记用作所需值的占位符

希望这可以帮助!

Marc Gravell的答案: C#字符串替换字典可以稍微更改,因此当找不到匹配时它不会引发exception。 在这种情况下,它根本不会取代匹配。

如果要替换的字符串是标记化的,这是解决方案:

 static readonly Regex RegExInstance = new Regex(@"\$(\w+)\$", RegexOptions.Compiled); public string ReplaceWithRegEx(string template, Dictionary parameters) { return RegExInstance.Replace(template, match => GetNewValue(parameters, match)); } private string GetNewValue(Dictionary parameters, Match match) { var oldValue = match.Groups[1].Value; string newValue; var found = parameters.TryGetValue(oldValue, out newValue); if (found) { return newValue; } var originalValue = match.Groups[0].Value; return originalValue; } 

我用100.000字节的字符串,7个密钥和数百个替换测试了该解决方案。 它使用的内存比字符串的长度多7倍。 它只花了0.002秒。