2009-08-05 133 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