2012-01-26 16 views
9

Nói rằng tôi có một mẫu văn bản với một số lĩnh vực mà cần phải được dân cư:Efficient mẫu dân

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

và một IDictionary<string,string> các giá trị thay thế:

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

Cách ngây thơ của Populating mẫu có thể giống như sau:

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

Tuy nhiên, điều này có vẻ không hiệu quả. Có cách nào tốt hơn?

+0

Tôi có thể hỏi những gì đã tình huống buộc bạn phải làm điều này thay vì một chuỗi "bình thường". –

+0

@BrankoDimitrijevic: Một loạt các mẫu email có thể chỉnh sửa của người dùng – spender

Trả lời

4

Bạn có thể sử dụng một Regex.Replace(), như thế này:

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

AFAIK này cũng sẽ ngăn kết quả bất ngờ nếu từ điển của bạn được xây dựng như thế này, bởi vì điều này có thể tạo ra "hello UK. you are 38 years old. you live in UK" cũng như "hello {$location}. you are 38 years old. you live in UK", vì dictionarys không sắp xếp khóa của họ:

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

Khi hành vi đầu tiên thực sự là mong muốn, bạn chỉ có thể chạy regex nhiều lần.

Chỉnh sửa: Nếu phân tích mẫu thực sự trong phần thời gian quan trọng của mã, không làm mẫu phân tích cú pháp ở đó. bạn nên cân nhắc sử dụng phương pháp phân tích thủ công Sean được khuyến nghị.

+0

không dễ đọc, tôi thích phương thức của người cho vay :) – vulkanino

+1

@vulkanino: Cách tiếp cận của OP có thể dễ đọc hơn, nhưng không an toàn đối với các biến chứa mã thông báo hợp lệ. Và nếu bạn có một bộ rất lớn các thẻ có thể, nhưng chỉ sử dụng một hoặc hai trong số chúng trong mẫu của bạn, vòng lặp trên tất cả các mã thông báo có thể chắc chắn sẽ chậm hơn. – Nuffin

+0

Tôi thích điều này rất nhiều. Đã hoàn toàn quên về MatchEvaluator. Tốt đẹp. – spender

-2

Lúc nguy cơ sounding ngớ ngẩn, bạn chỉ có thể viết một hàm để trả về chuỗi bạn muốn:

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

Vì bạn chỉ có thể lưu trữ một tập hợp các giá trị có trong từ điển, giá trị của việc sử dụng một mẫu theo cách này có vẻ giảm đi.

+0

Có. Rất ngớ ngẩn. Điều đó hoàn toàn không thể sử dụng được. Cố gắng chống lại một downvote. – spender

+0

Tôi không muốn xem mã sản xuất mà cách tiếp cận này được sử dụng và có một số mẫu và có thể có hàng trăm biến có sẵn ... – Nuffin

+0

@Tobias: Đồng ý. Đối với các tập lớn, điều này sẽ là xấu. Tôi đoán tôi đã tập trung vào ba trường hợp biến. – JayP

4

Không có gì sai với cách tiếp cận của bạn, nó phụ thuộc vào ngữ cảnh đang được sử dụng. Ví dụ, trong một vòng lặp quan trọng nhiệm vụ chặt chẽ nó không phải là cách tiếp cận hiệu quả nhất, nhưng thỉnh thoảng sử dụng, hoặc trong một gui nó có lẽ là ok.

Giải pháp hiệu quả hơn là phân tích cú pháp chuỗi. Ví dụ. tìm kiếm { đầu tiên và sau đó cho } tiếp theo. Văn bản giữa chúng là chìa khóa để tra cứu, sau đó bạn có thể thay thế. Sau đó, bạn bắt đầu với tìm kiếm từ ký tự sau số }. Ưu điểm của phương pháp này là nếu giá trị bạn chèn có mã thông báo được nhúng, nó sẽ không được thay thế. Điểm bất lợi là khó xử lý các trường hợp cạnh khi phân tích cú pháp.

+0

+1: Tôi nghĩ đây không phải là vấn đề. – vulkanino

1

Sử dụng một biểu thức chính quy phù hợp với một specifier lĩnh vực:

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

Regex giải thích:

  1. một chữ {
  2. một chữ $ (trong đó có đến được thoát)
  3. một nhóm được chụp () chứa:
    1. phi } ký tự
    2. một hoặc nhiều trong số họ +
    3. dùng càng ít càng tốt ? (chụp uể oải)
  4. một chữ }

trận đấu regex này so với mẫu, sử dụng công cụ đánh giá tùy chỉnh thay thế trong giá trị trường có liên quan:

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

Bạn có thể loại bỏ lambda này thành một phương pháp kiểm tra xem trường thực sự tồn tại, nếu bạn muốn.

0

Nếu bạn đang lo lắng về hiệu suất, tự phân tích mẫu trong một pass duy nhất có lẽ là nhanh nhất bạn có thể đi:

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

    } 

} 

Sử dụng nó như thế này:

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); 
Các vấn đề liên quan