【问题标题】:C# String replace with dictionaryC#字符串替换为字典
【发布时间】:2010-11-16 22:27:05
【问题描述】:

我有一个字符串,我需要对其进行一些替换。我有一个Dictionary<string, string>,我在其中定义了搜索替换对。我创建了以下扩展方法来执行此操作:

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;
}

有没有更好的方法?

【问题讨论】:

    标签: c# string dictionary replace


    【解决方案1】:

    如果数据被标记化(即“亲爱的 $name$,截至 $date$,您的余额为 $amount$”),那么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 真的有问题吗?

    【讨论】:

    • 很好的答案。我认为您的建议实际上会比遍历整个字典更好,因为正则表达式只会替换找到的标记。它不会检查可能在里面的每一个。因此,如果我在输入字符串中有一个很大的字典和少量的标记,这实际上可以提升我的应用程序。
    • 非常有用。我将它重构为 Regex 的扩展方法,我无法在评论中显示,因此将在下面添加一些多余的额外答案。
    • 如果没有找到key会抛出异常。
    • 如果找不到密钥,我们有什么解决办法吗?
    • @Phoenix_uy match =&gt; args.TryGetValue(match.Groups[1].Value, out var val) ? val : "*whatever*"
    【解决方案2】:

    使用 Linq 执行此操作:

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

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

    str 是您需要进行一些替换的字符串。

    【讨论】:

      【解决方案3】:

      对我来说似乎是合理的,除了一件事:它是顺序敏感的。例如,输入字符串“$x $y”和替换字典:

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

      替换的结果是要么“foo foo”或“$y foo”,具体取决于先执行哪个替换。

      您可以改用List&lt;KeyValuePair&lt;string, string&gt;&gt; 来控制排序。另一种方法是遍历字符串,确保您不会在进一步的替换操作中使用替换。不过,这可能要困难得多。

      【讨论】:

        【解决方案4】:

        这里是@Marc 的出色答案的轻微重构版本,以使该功能可用作 Regex 的扩展方法:

        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]);
            }
        }
        

        【讨论】:

          【解决方案5】:

          在使用 Marc Gravell 的 RegEx 解决方案时,首先使用 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"}
                  };
          

          这会产生以下结果:

          “亲爱的 $name$,截至 2009 年 8 月 5 日,您的余额为 200 英镑”

          【讨论】:

            【解决方案6】:

            你在这里:

            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();
                }
            }
            

            【讨论】:

            • 您可以在您的答案中添加一些解释或评论,这样读者就不必仔细检查代码来理解您的建议。特别是因为这是一个相当长的 sn-p,比其他提议的解决方案要长得多。
            • 它做了作者想做的事。与其他不同的是,它不使用正则表达式,并且只通过该行一次,这在您需要产生几十个替换时很重要。
            • 这看起来像是忽略了单词边界并且似乎很容易出错。另一方面,没有任何解释,所以我可能错了。
            • 是的,没有检查单词边界。
            【解决方案7】:

            此特定任务的正确工具是 Mustache,一种简单的标准无逻辑模板语言。

            有很多实现。对于这个例子,我将使用stubble

            var stubble = new StubbleBuilder().Build();
            var dataHash = new Dictionary<string, Object>()
            {
                {"Foo","My Foo Example"},
                {"Bar",5}
            };
            
            var output = stubble.Render(
               "Hey, watch me replace this: {{Foo}} ... with example text.  Also {{bar}} is 5"
               , dataHash
            );
            

            【讨论】:

              【解决方案8】:

              为什么不检查这样的密钥是否存在?

              • 如果存在,则删除该对,否则,跳过此步骤;

              • 添加相同的键,但现在具有新的所需值。

                  // say, you have the following collection
                  var fields = new Dictionary<string, string>();
                  fields.Add("key1", "value1");
                  fields.Add("key2", "value2");
                  fields.Add("key3", "value3");
                
                  // now, you want to add a pair "key2"/"value4"
                  // or replace current value of "key2" with "value4"
                  if (fields.ContainsKey("key2"))
                  {
                      fields.Remove("key2");
                  }
                  fields.Add("key2", "value4");
                

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2014-09-02
                • 2013-11-18
                • 1970-01-01
                • 2016-06-12
                • 2017-07-15
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多