2009-08-05 171 views
22

我有一个字符串,我需要做一些替换。我有一个Dictionary<string, string>,我有定义的搜索替换对。我已经创建了以下扩展方法来执行此操作:用字典替换C#字符串

public static string Replace(this string str, Dictionary<string, string> dict) 
{ 
    StringBuilder sb = new StringBuilder(str); 

    return sb.Replace(dict).ToString(); 
} 

public static StringBuild Replace(this StringBuilder sb, 
    Dictionary<string, string> dict) 
{ 
    foreach (KeyValuePair<string, string> replacement in dict) 
    { 
     sb.Replace(replacement.Key, replacement.Value); 
    } 

    return sb; 
} 

有没有更好的方法来做到这一点?

回答

38

如果数据标记化(即“亲爱的$名字$,如$日期$的您的余额是$金额$”),那么Regex可能是有用的:

static readonly Regex re = new Regex(@"\$(\w+)\$", RegexOptions.Compiled); 
static void Main() { 
    string input = @"Dear $name$, as of $date$ your balance is $amount$"; 

    var args = new Dictionary<string, string>(
     StringComparer.OrdinalIgnoreCase) { 
      {"name", "Mr Smith"}, 
      {"date", "05 Aug 2009"}, 
      {"amount", "GBP200"} 
     }; 
    string output = re.Replace(input, match => args[match.Groups[1].Value]); 
} 

然而,如果没有像这一点,我期望你的Replace循环可能和你所能做的一样多,而不需要太长的时间。如果它没有被标记,可能会描述它;实际上是Replace有问题吗?

+0

伟大的答案。我认为你的提议实际上会更好,然后迭代整个词典,因为正则表达式只会替换找到的记号。它不会检查每一个可能在里面。所以如果我在输入字符串中有一个大的字典和少量的标记,实际上可以给我的应用程序一个提升。 – RaYell 2009-08-05 08:28:39

+0

非常有用。我将它重构为正则表达式的扩展方法,我无法在评论中显示,所以会在下面添加一些额外的答案。 – 2013-03-20 16:20:59

+1

如果找不到密钥,这将引发异常。 – Axel 2017-01-15 05:54:38

9

对我来说似乎是合理的,除了一件事情:它是顺序敏感的。取决于哪个替换

"$x" => "$y" 
"$y" => "foo" 

替换的结果是要么“富富”或“$ y的foo”的:例如,以“$ X $ Y”的输入字符串和的替换字典首先执行。

您可以改为使用List<KeyValuePair<string, string>>来控制排序。另一种方法是穿过绳子,确保在进一步的替换操作中不会消耗替换物。但这可能会困难很多。

12

使用LINQ这样做:

var newstr = dict.Aggregate(str, (current, value) => 
    current.Replace(value.Key, value.Value)); 

字典是你的搜索替换定义字典对象对。

STR是你的字符串,你需要做一些与替代。

+0

+1我过去曾经使用过正则表达式,但是这对我很好。 – clairestreb 2014-10-17 22:16:52

4

这里是@马克的伟大答案轻度重因素的版本,使作为一个扩展可用的方法正则表达式的功能:

static void Main() 
{ 
    string input = @"Dear $name$, as of $date$ your balance is $amount$"; 
    var args = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); 
    args.Add("name", "Mr Smith"); 
    args.Add("date", "05 Aug 2009"); 
    args.Add("amount", "GBP200"); 

    Regex re = new Regex(@"\$(\w+)\$", RegexOptions.Compiled); 
    string output = re.replaceTokens(input, args); 

    // spot the LinqPad user // output.Dump(); 
} 

public static class ReplaceTokensUsingDictionary 
{ 
    public static string replaceTokens(this Regex re, string input, IDictionary<string, string> args) 
    { 
     return re.Replace(input, match => args[match.Groups[1].Value]); 
    } 
} 
2

使用马克·Gravell的正则表达式的解决方案时,首先检查如果一个令牌可用即,使用的containsKey,这防止KeyNotFoundException错误:

string output = re.Replace(zpl, match => { return args.ContainsKey(match.Groups[1].Value) ? arg[match.Groups[1].Value] : match.Value; }); 
使用以下略作修改的示例代码时

(第一参数具有不同的名称):

var args = new Dictionary<string, string>(
     StringComparer.OrdinalIgnoreCase) 
     { 
      {"nameWRONG", "Mr Smith"}, 
      {"date", "05 Aug 2009"}, 
      {"AMOUNT", "GBP200"} 
     }; 

这将产生以下:

“亲爱的$ $名,截至2009年08月05日的余额为GBP200的”

0

给你:

public static class StringExm 
{ 
    public static String ReplaceAll(this String str, KeyValuePair<String, String>[] map) 
    { 
     if (String.IsNullOrEmpty(str)) 
      return str; 

     StringBuilder result = new StringBuilder(str.Length); 
     StringBuilder word = new StringBuilder(str.Length); 
     Int32[] indices = new Int32[map.Length]; 

     for (Int32 characterIndex = 0; characterIndex < str.Length; characterIndex++) 
     { 
      Char c = str[characterIndex]; 
      word.Append(c); 

      for (var i = 0; i < map.Length; i++) 
      { 
       String old = map[i].Key; 
       if (word.Length - 1 != indices[i]) 
        continue; 

       if (old.Length == word.Length && old[word.Length - 1] == c) 
       { 
        indices[i] = -old.Length; 
        continue; 
       } 

       if (old.Length > word.Length && old[word.Length - 1] == c) 
       { 
        indices[i]++; 
        continue; 
       } 

       indices[i] = 0; 
      } 

      Int32 length = 0, index = -1; 
      Boolean exists = false; 
      for (int i = 0; i < indices.Length; i++) 
      { 
       if (indices[i] > 0) 
       { 
        exists = true; 
        break; 
       } 

       if (-indices[i] > length) 
       { 
        length = -indices[i]; 
        index = i; 
       } 
      } 

      if (exists) 
       continue; 

      if (index >= 0) 
      { 
       String value = map[index].Value; 
       word.Remove(0, length); 
       result.Append(value); 

       if (word.Length > 0) 
       { 
        characterIndex -= word.Length; 
        word.Length = 0; 
       } 
      } 

      result.Append(word); 
      word.Length = 0; 
      for (int i = 0; i < indices.Length; i++) 
       indices[i] = 0; 
     } 

     if (word.Length > 0) 
      result.Append(word); 

     return result.ToString(); 
    } 
} 
+1

您可以在答案中添加一些解释或评论,以便读者不必仔细检查代码,以了解您提出的建议。特别是因为这是一个相当长的片段,比其他提议的解决方案长得多。 – 2016-04-21 23:59:27

+0

它做作者想要的。与其他不同的是,它不使用正则表达式,并且只经过一次线,这在您需要生成几十次替换时非常重要。 – Albeoris 2016-04-23 07:10:00

+0

这看起来像忽略了单词边界,似乎很容易出错。另一方面,没有解释,所以我可能是错的。 – 2016-05-02 11:50:04