2012-01-26 49 views
9

說我有一個數字,需要填充字段的文本模板:高效模板羣體

var template = "hello {$name}. you are {$age} years old. you live in {$location}" 

和值的IDictionary<string,string>替代:

key  | value 
=================== 
name | spender 
age  | 38 
location| UK 

填充的幼稚方式該模板可能是這樣的:

var output = template; 
foreach(var kvp in templValues) 
{ 
    output = output.Replace(string.format("{{${0}}}", kvp.Key), kvp.Value); 
} 

但是,這似乎痛苦地低效率。有沒有更好的辦法?

+0

請問是什麼這種情況迫使你這樣做,而不是一個「正常」的字符串。格式? –

+0

@BrankoDimitrijevic:一堆用戶可編輯的電子郵件模板 – spender

回答

4

你可以使用一個Regex.Replace(),像這樣:

var output = new Regex(@"\{\$([^}]+)\}").Replace(
    template, 
    m => templValues.ContainsKey(m.Captures[1].Value) 
     ? templValues[m.Captures[1].Value] 
     : m.Value); 

AFAIK這也將防止意外的結果,如果你的字典是建立這樣的,因爲這可能會產生"hello UK. you are 38 years old. you live in UK"以及"hello {$location}. you are 38 years old. you live in UK",因爲dictionarys不排序鑰匙:

key  | value 
=================== 
name | {$location} 
age  | 38 
location| UK 

當第一行爲實際需要,您可以只運行正則表達式多次。

編輯:如果模板解析實際上是在代碼的時間關鍵部分,不在那裏做模板解析。你應該考慮使用推薦的手動解析方法Sean

+0

不太可讀,我更喜歡使用方法:) – vulkanino

+1

@vulkanino:OP的方法可能更具可讀性,但它對包含有效標記的變量本身並不安全。如果你有一個非常大的可能的令牌集,但是在你的模板中只使用其中的一個或兩個,那麼在所有可能的令牌上循環肯定是慢的。 – Nuffin

+0

我很喜歡這個。已經完全遺忘了MatchEvaluator。尼斯。 – spender

-2

在探空愚蠢的風險,你可以只寫一個函數來返回你想要的字符串:

public string CreateString(string name, string age, string location) 
{ 
    return "hello " + name + ". you are " + age + " years old. you live in " + location; 
} 

既然你只能存儲一組值在詞典中,使用模板的價值這種方式似乎減少了。

+0

是的。非常愚蠢。這完全無法使用。試圖抵制downvote。 – spender

+0

我不想看到使用這種方法的生產代碼,並且有幾個模板,可能有數百個可用變量... – Nuffin

+0

@Tobias:同意。對於大型變量集合,這將是不好的。我想我太專注於三個可變情況。 – JayP

4

你的方法沒有問題,它取決於它使用的上下文。例如,在嚴格的關鍵任務循環中,它不是最有效的方法,但是對於偶爾使用,或者在gui中它可能沒問題。

更有效的解決方案是解析字符串。例如。搜索第一個{,然後搜索下一個}。它們之間的文本是查找的關鍵,然後您可以替換它。然後,您從}之後的字符開始搜索。這種方法的優點是,如果您插入的值具有嵌入式令牌,則不會被替換。缺點是解析時處理邊緣案例更加困難。

+0

+1:我認爲這是一個沒有問題的問題。 – vulkanino

1

使用的正則表達式匹配的字段說明符:

var fieldRegex = new Regex(@"{\$([^}]+?)}", RegexOptions.Compiled); 

正則表達式的解釋:

  1. 字面{
  2. 字面$(其具有被轉義)
  3. 一個捕獲組()包含:
    1. }字符
    2. 其中一個或多個+
    3. 以儘可能少的?的(懶洋洋地捕捉)
  4. 文字}

匹配此正則表達式與模板,使用替代相關字段值的自定義評估程序:

var template = "hello {$name}. you are {$age} years old. you live in {$location}"; 

var fieldValues = new Dictionary<string, string> 
         { 
          { "name", "spender" }, 
          { "age", "38" }, 
          { "location", "UK" }, 
         }; 

var output = fieldRegex.Replace(
    template, 
    match => fieldValues[match.Groups[1].Value]); 

你可以將這個lambda分解成一個方法來檢查字段是否真的存在,如果你想的話。

0

如果你擔心性能,手動解析在單次的模板可能是最快的,你可以去:

static string DictFormat(string template, IDictionary<string, string> dict) { 

    const string left_delimiter = "{$"; 
    int left_delimiter_len = left_delimiter.Length; 
    const string right_delimiter = "}"; 
    int right_delimiter_len = right_delimiter.Length; 

    var sb = new StringBuilder(); 

    int end = 0; 
    while (true) { 

     int start = template.IndexOf(left_delimiter, end); 
     if (start >= 0) { 
      sb.Append(template.Substring(end, start - end)); 
      start += left_delimiter_len; 
      end = template.IndexOf(right_delimiter, start); 
      if (end >= 0) { 
       string key = template.Substring(start, end - start); 
       string value; 
       if (dict.TryGetValue(key, out value)) { 
        sb.Append(value); 
        end += right_delimiter_len; 
       } 
       else 
        throw new ArgumentException(string.Format("Key not found: {0}", key), "template"); 
      } 
      else 
       throw new ArgumentException(string.Format("Key starting at {0} not properly closed.", start), "template"); 
     } 
     else { 
      sb.Append(template.Substring(end)); 
      return sb.ToString(); 
     } 

    } 

} 

使用方法如下:

const string template = "hello {$name}. you are {$age} years old. you live in {$location}"; 
var dict = new Dictionary<string, string> { { "name", "spender" }, { "age", "38" }, { "location", "UK" } }; 
string result = DictFormat(template, dict);