2012-01-26 13 views
9

dice che ho un modello di testo con un numero di campi che devono essere popolato:efficiente modello di popolazione

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

ed un IDictionary<string,string> di valori da sostituire:

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

Il modo ingenuo di popolamento il modello potrebbe essere qualcosa del tipo:

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

Tuttavia, questo sembra dolorosamente inefficiente. C'è un modo migliore?

+0

Posso chiederle che cosa era la situazione che ti ha costretto a farlo invece di una stringa "normale". Formatta? –

+0

@BrankoDimitrijevic: Un mucchio di modelli di email modificabili dall'utente – spender

risposta

4

È possibile utilizzare un Regex.Replace(), come questo:

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

per quanto ne so questo sarà anche evitare risultati imprevisti se il dizionario è costruito in questo modo, perché questo potrebbe produrre "hello UK. you are 38 years old. you live in UK" così come "hello {$location}. you are 38 years old. you live in UK", dal momento che non lo fanno dictionarys Ordina le loro chiavi:

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

Quando il primo comportamento è in realtà desidera, è possibile semplicemente eseguire le più volte regex.

Edit: Se il modello di analisi è in realtà in una parte momento critico del codice, non faccio modello di analisi lì. si consiglia di utilizzare il metodo di analisi manuale Sean consigliato.

+0

non molto leggibile, preferisco il metodo dello spender :) – vulkanino

+1

@vulkanino: L'approccio dell'OP potrebbe essere più leggibile, ma non è sicuro per quanto riguarda le variabili che contengono token validi. E se hai un set molto grande di token possibili, ma ne usi solo uno o due nel tuo template, il loop su tutti i possibili token è decisamente più lento. – Nuffin

+0

Mi piace molto questo. Si era completamente dimenticato di MatchEvaluator. Bello. – spender

-2

A rischio di sembrare sciocco, si può solo scrivere una funzione per restituire la stringa che si desidera:

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

Dal momento che è possibile memorizzare solo una serie di valori nel dizionario, il valore di usare un modello in questo modo sembra diminuito.

+0

Sì. Molto sciocco. Questo è assolutamente inutile. Cercando di resistere a un downvote. – spender

+0

Non voglio vedere il codice di produzione in cui viene utilizzato questo approccio e ci sono diversi modelli e probabilmente diverse centinaia di variabili disponibili ... – Nuffin

+0

@Tobias: concordato. Per set di variabili grandi questo sarebbe male. Immagino di essere troppo concentrato sul caso delle tre variabili. – JayP

4

Non c'è niente di sbagliato nel vostro approccio, dipende dal contesto in cui viene utilizzato. Ad esempio, in un ciclo strettamente critico e mission-critical non è l'approccio più efficiente, ma per un uso occasionale, o in un gui, probabilmente è ok.

Una soluzione più efficiente sarebbe quella di analizzare la stringa. Per esempio. cercare il primo { e poi il successivo }. Il testo tra loro è la chiave per la ricerca, che puoi quindi sostituire. Inizia quindi con la ricerca dal personaggio dopo lo }. Il vantaggio di questo approccio è che se il valore inserito ha un token incorporato, non verrà sostituito. Lo svantaggio è che è più difficile gestire i casi limite durante l'analisi.

+0

+1: Penso che questo non sia un problema. – vulkanino

1

usare un'espressione regolare che corrisponde a un identificatore di campo:

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

Regex spiegazione:

  1. un letterale {
  2. un letterale $ (che deve essere sfuggito)
  3. un gruppo catturato () contenente:
    1. non } caratteri
    2. uno o più di essi +
    3. prendendo il meno possibile ? (cattura pigramente)
  4. un letterale }

corrispondono a questa espressione regolare contro il modello, utilizzando un valutatore personalizzato che sostituisce il valore del campo pertinente:

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

È possibile suddividere questo lambda in un metodo che controlla se il campo esiste effettivamente, se lo si desidera.

0

Se siete preoccupati per le prestazioni, l'analisi manualmente il modello in un unico passaggio è probabilmente il più veloce si può andare:

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

    } 

} 

Utilizzare in questo modo:

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); 
Problemi correlati