2008-12-18 5 views
7

ich eine gute Leistung Art und Weise wollen (hoffe ich) ersetzen einen benannten Parameter in meinem String in einen benannten Parameter von Code, beispielsweise mein string:C# Benannte Parameter auf einen String, auf den Parameter ersetzen Werte

"Hi {name}, do you like milk?" 

Wie kann ich {Name} durch Code ersetzen, Reguläre Ausdrücke? Zu teuer? Welchen Weg empfehlen Sie?

Wie ersetzen sie in Beispiel NHibernates HQL: my_param zu dem benutzerdefinierten Wert? Oder in ASP.NET (MVC) Routing, das mir besser gefällt, "{controller}/{action}", new {controller = "Hallo", ...}?

Antwort

20

Haben Sie bestätigt, dass reguläre Ausdrücke zu teuer sind?

Die Kosten für reguläre Ausdrücke sind stark übertrieben. Für solch ein einfaches Muster wird die Leistung ziemlich gut sein, wahrscheinlich nur etwas weniger gut als das direkte Suchen und Ersetzen in der Tat. Haben Sie auch mit dem Flag Compiled beim Erstellen des regulären Ausdrucks experimentiert?

Das gesagt, können Sie nicht einfach den einfachsten Weg verwenden, d. H. Replace?

+1

+1 für die Dinge auf die einfache Art und Weise :) –

+1

(Obwohl Sie bedeuten, ersetzen (Muster, "Mike")) –

+5

@Jon: Danke. Warum einen Compiler verwenden, wenn du einen Jon hast? ;-) –

0

Eine kompilierte Regex könnte den Trick machen, besonders wenn viele Tokens ersetzt werden sollen. Wenn es nur eine Handvoll von ihnen gibt und die Leistung der Schlüssel ist, würde ich das Token einfach per Index finden und durch Zeichenfolgenfunktionen ersetzen. Glauben Sie es oder nicht, das wird schneller als eine Regex sein.

13

Regex ist sicherlich ein gangbarer Weg, vor allem mit einem MatchEvaluator:

Regex re = new Regex(@"\{(\w*?)\}", RegexOptions.Compiled); // store this... 

    string input = "Hi {name}, do you like {food}?"; 

    Dictionary<string, string> vals = new Dictionary<string, string>(); 
    vals.Add("name", "Fred"); 
    vals.Add("food", "milk"); 

    string q = re.Replace(input, delegate(Match match) 
    { 
     string key = match.Groups[1].Value; 
     return vals[key]; 
    }); 
+0

Verdammt ... Wie hast du das 18 Minuten vor mir bekommen? –

+0

Wenn Sie .NET 3.5 verwenden, können Sie das Delegat-Schlüsselwort beenden. Delegat (Match-Match) kann sein Match => –

+0

@scalvert - um genau zu sein, das ist C# 3.0, nicht .NET 3.5; Es würde auch auf .NET 2.0 mit C# 3.0 ausgerichtet sein. –

3

Nun, wenn Sie Sie Ersatz in einem Wörterbuch haben, wie folgt aus:

var replacements = new Dictionary<string, string>(); 
    replacements["name"] = "Mike"; 
    replacements["age"]= "20"; 

dann die Regex wird ganz einfach:

Regex regex = new Regex(@"\{(?<key>\w+)\}"); 
    string formattext = "{name} is {age} years old"; 
    string newStr = regex.Replace(formattext, 
      match=>replacements[match.Groups[1].Captures[0].Value]); 
+1

+1 für die Kürze - von allen beigetragenen Lösungen, das ist mein persönlicher Favorit :-) –

+0

hasse Regexes, aber das ist ziemlich schön. –

0

Versuchen Sie es mit StringTemplate. Es ist viel mächtiger als das, aber es macht den Job fehlerlos.

1

Wie wäre es

stringVar = "Hello, {0}. How are you doing?"; 
arg1 = "John"; // or args[0] 
String.Format(stringVar, arg1) 

Sie sogar mehrere args haben kann, erhöhen Sie einfach die {x} und andere Parameter an die Format() Methode hinzufügen. Nicht sicher, dass die verschiedenen aber "string" und "String" diese Methode haben.

+1

er braucht benannte Parameter, nicht geordnete. –

2

Nachdem ich darüber nachgedacht hatte, realisierte ich, was ich mir eigentlich wünschte, dass String.Format() ein IDictionary als Argument nehmen würde, und dass Templates mit Namen anstelle von Indizes geschrieben werden könnten.

Bei String-Substitutionen mit vielen möglichen Schlüsseln/Werten führen die Indexnummern zu unleserlichen String-Vorlagen - und in einigen Fällen wissen Sie vielleicht nicht einmal, welche Artikel welche Nummer haben werden Erweiterung:

https://gist.github.com/896724

Grundsätzlich können Sie auf diese Zeichenketten-Templates mit Namen statt Nummern und ein Wörterbuch anstelle eines Arrays, verwenden und können Sie alle anderen guten Eigenschaften von String haben.Format(), die Verwendung eines benutzerdefinierten IFormatProvider, falls erforderlich, und die Verwendung aller üblichen Formatierungssyntax - Genauigkeit, Länge, etc.

Die example im Referenzmaterial für String.Format ist eine große Beispiel dafür, wie Vorlagen mit vielen nummerierten Elemente geworden völlig unleserlich - das Beispiel der Portierung dieser neuen Erweiterung Methode zu verwenden, können Sie so etwas wie dieses:

var replacements = new Dictionary<String, object>() 
         { 
          { "date1", new DateTime(2009, 7, 1) }, 
          { "hiTime", new TimeSpan(14, 17, 32) }, 
          { "hiTemp", 62.1m }, 
          { "loTime", new TimeSpan(3, 16, 10) }, 
          { "loTemp", 54.8m } 
         }; 

var template = 
    "Temperature on {date1:d}:\n{hiTime,11}: {hiTemp} degrees (hi)\n{loTime,11}: {loTemp} degrees (lo)"; 

var result = template.Subtitute(replacements); 

Als jemand darauf hingewiesen, wenn das, was Sie Bedürfnisse gerade schreiben hoch sein Optimiert, verwenden Sie nicht so etwas - Wenn Sie Millionen von Strings auf diese Weise in einer Schleife formatieren müssen, könnte der Speicher- und Performance-Overhead erheblich sein.

Auf der anderen Seite, wenn Sie besorgt über lesbare, wartbare Code schreiben - und wenn Sie, sagen wir, eine Reihe von Datenbank-Operationen, im großen Schema der Dinge, diese Funktion wird keine signifikanten hinzufügen Overhead.

...

Der Einfachheit halber habe ich versucht, eine Methode hinzuzufügen, die ein anonymes Objekt anstelle eines Wörterbuchs akzeptieren würde:

public static String Substitute(this String template, object obj) 
{ 
    return Substitute(
     template, 
     obj.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(obj, null)) 
    ); 
} 

Aus irgendeinem Grund, dies nicht funktioniert - Weitergabe Ein anonymes Objekt wie new { name: "value" } zu dieser Erweiterungsmethode gibt eine Fehlermeldung zur Kompilierungszeit, die besagt, dass die IDictionary-Version der Methode die beste Übereinstimmung ist. Nicht sicher, wie das zu beheben ist. (jemand?)

+0

Haben Sie jemals den anonymen Typ herausgefunden? das wäre echt cool. –

+0

Ich erinnere mich nicht - das war vor langer Zeit, und ich arbeite derzeit nicht mit ASP.NET ... Ich schaute auf den Code von damals, und es ging in die Produktion sieht genauso aus - ich erinnere mich vage an die Problem irgendwie nicht mit diesem Code, aber mit dem Consumer-Code. Das ist der beste Hinweis, den ich dir geben kann. –

0

oder versuchen Sie dies mit Linq, wenn Sie alle Ihre Ersetzungswerte in einem Dictionary Obj gespeichert haben.

Zum Beispiel:

Dictionary<string,string> dict = new Dictionary<string,string>(); 
dict.add("replace1","newVal1"); 
dict.add("replace2","newVal2"); 
dict.add("replace3","newVal3"); 

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

dict ist Ihre Suche-Ersetzen-Paare Dictionary-Objekt definiert. str ist Ihre Zeichenfolge, mit der Sie einige Ersetzungen vornehmen müssen.

0

Ich würde für die mindplay.dk Lösung gehen ... Funktioniert ganz gut.

Und mit einer geringfügigen Änderung, es unterstützt Vorlagen-of-Vorlagen, wie "Hallo {Name}, mögen Sie {0}?", Ersetzt {name} aber beibehalten {0}:

in der gegebenen Quelle (https://gist.github.com/896724), wie folgt ersetzen:

 var format = Pattern.Replace(
      template, 
      match => 
       { 
        var name = match.Groups[1].Captures[0].Value; 

        if (!int.TryParse(name, out parsedInt)) 
        { 
         if (!map.ContainsKey(name)) 
         { 
          map[name] = map.Count; 
          list.Add(dictionary.ContainsKey(name) ? dictionary[name] : null); 
         } 

         return "{" + map[name] + match.Groups[2].Captures[0].Value + "}"; 
        } 
        else return "{{" + name + "}}"; 
       } 
      ); 

Darüber hinaus unterstützt es eine Länge ({Name, 30}) sowie eine formatspecifier oder eine Kombination von beidem.