Regulärer Ausdruck für String.Format-Dienstprogramm wie
Ich Schreibe eine Klasse namens StringTemplate
, die es ermöglicht, format-Objekte wie mit String.Format
, aber mit Namen statt Indizes für Platzhalter. Hier ist ein Beispiel :
string s = StringTemplate.Format("Hello {Name}. Today is {Date:D}, and it is {Date:T}.",
new { Name = "World", Date = DateTime.Now });
Um dieses Ergebnis zu erzielen, Suche ich nach Platzhaltern und ersetzen Sie Sie mit Indizes. Ich habe dann übergeben Sie das resultierende format-string zu String.Format
.
Dies funktioniert gut, außer wenn es verdoppelt Klammern, die eine escape-Sequenz. Das gewünschte Verhalten (das ist das gleiche wie String.Format
) wird unten beschrieben :
- "Hallo {Name}" sollte formatiert werden als "Hallo Welt"
- "Hallo {{Name}}" sollte formatiert werden als "Hallo {Name}"
- "Hallo {{{Name}}}" sollte formatiert werden als "Hallo {Welt}"
- "Hallo {{{{Name}}}}" sollte formatiert werden als "Hallo {{Name}}"
Und so weiter...
Aber meine aktuellen regulären Ausdruck nicht erkennen, die escape Sequenz ein, und berücksichtigt immer den Teilstring zwischen eckigen Klammern als Platzhalter, so dass ich Dinge wie "Hallo {0}".
Hier ist meine aktuelle regulärer Ausdruck :
private static Regex _regex = new Regex(@"{(?<key>\w+)(?<format>:[^}]+)?}", RegexOptions.Compiled);
Wie kann ich das ändern dieser reguläre Ausdruck zu ignorieren entgangen Zahnspange ? Was scheint wirklich so hart ist, dass ich erkennen sollte, Platzhalter, je nachdem, ob die Anzahl der Klammern gerade oder ungerade ist... ich kann nicht denken, der eine einfache Möglichkeit, es zu tun mit einem regulären Ausdruck verwenden, ist es überhaupt möglich ?
Vollständigkeit halber, hier der komplette code der StringTemplate
Klasse :
public class StringTemplate
{
private string _template;
private static Regex _regex = new Regex(@"{(?<key>\w+)(?<format>:[^}]+)?}", RegexOptions.Compiled);
public StringTemplate(string template)
{
if (template == null)
throw new ArgumentNullException("template");
this._template = template;
}
public static implicit operator StringTemplate(string s)
{
return new StringTemplate(s);
}
public override string ToString()
{
return _template;
}
public string Format(IDictionary<string, object> values)
{
if (values == null)
{
throw new ArgumentNullException("values");
}
Dictionary<string, int> indexes = new Dictionary<string, int>();
object[] array = new object[values.Count];
int i = 0;
foreach (string key in values.Keys)
{
array[i] = values[key];
indexes.Add(key, i++);
}
MatchEvaluator evaluator = (m) =>
{
if (m.Success)
{
string key = m.Groups["key"].Value;
string format = m.Groups["format"].Value;
int index = -1;
if (indexes.TryGetValue(key, out index))
{
return string.Format("{{{0}{1}}}", index, format);
}
}
return string.Format("{{{0}}}", m.Value);
};
string templateWithIndexes = _regex.Replace(_template, evaluator);
return string.Format(templateWithIndexes, array);
}
private static IDictionary<string, object> MakeDictionary(object obj)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
foreach (var prop in obj.GetType().GetProperties())
{
dict.Add(prop.Name, prop.GetValue(obj, null));
}
return dict;
}
public string Format(object values)
{
return Format(MakeDictionary(values));
}
public static string Format(string template, IDictionary<string, object> values)
{
return new StringTemplate(template).Format(values);
}
public static string Format(string template, object values)
{
return new StringTemplate(template).Format(values);
}
}
- Ich ging vor kurzem auf diesem Weg mich selbst, und am Ende war es viel einfacher zu bauen ist eine einfache state machine, die ausgewertet Platzhalter als es war, um herauszufinden, wie man eine regex zu arbeiten. Auch wenn ich endete mit meinen Trennzeichen die eckigen Klammern (weil
FlowDocument
hat einige nicht-sehr-gut-dokumentierten Verhaltensweisen umgebenden geschweiften Klammern, die ich fing an zu laufen, in) gibt es eine Menge von Problemen, dass ich gar nicht auf, dass ich haben würde, wenn ich verwendet, regexes. - Ja, ich sollte vermutlich verwendet eine state-machine statt... aber egal, mein code funktioniert jetzt genau so, wie ich will, so werde ich nicht ändern, es sei denn ich 😉
- Werden Sie nach dem code, den Sie aufgewickelt mit?
- die jüngste version der Klasse hier. Es hat sich ziemlich geändert, seit ich gepostet dieser Nachricht 😉
Du musst angemeldet sein, um einen Kommentar abzugeben.
Können Sie einen regulären Ausdruck für eine ausgewogene pair-Mädchen, dann herauszufinden, was mit den geschweiften Klammern. Denken Sie daran, dass .NET regexs sind nicht die "normalen".
Kann es gut möglich sein, die mit regulären Ausdrücken - aber ich bin überhaupt nicht davon überzeugt, dass es die einfachste Lösung zu erhalten. Gegeben, dass Sie wirklich nur interessiert Klammern und Doppelpunkte, die hier (glaube ich), würde ich persönlich vermeiden Sie die Verwendung von regulären Ausdrücken.
Ich würde konstruieren eine Folge von Token, die jeweils entweder ein literal oder eine format-string. Konstrukt, das nur zu Fuß entlang der Zeichenfolge und bemerken, dass die öffnung und schließende Klammern. Dann die Auswertung der Sequenz ist nur eine Frage der Verkettung der Token, die Formatierung jeweils gegebenenfalls.
Dann wieder ich habe noch nie viel von einem fan von regulären Ausdrücken - nur gelegentlich sind Sie wunderbar, aber viel von der Zeit fühlen Sie sich wie overkill. Vielleicht gibt es einen cleveren Weg, um Sie zu tun, was Sie wollen, in diesem Fall...
Btw, Sie gehen zu müssen, um zu definieren, was geschehen soll, in Fällen, in denen die Klammern nicht passen, z.B.
Parität ist in der Regel sehr leicht zu entscheiden, anhand von regulären Ausdrücken. Dies ist ein Beispiel für einen Ausdruck, entspricht einer beliebigen Zeichenfolge mit einer geraden Anzahl von
A
s, aber keine ungerade Zahl:So alle Sie tun müssen ist, finden Sie den Ausdruck, der passt nur eine ungerade Anzahl von
{
s und}
s.(die Flucht der Charaktere trotz). So das hinzufügen dieser Idee, Sie aktuelle Ausdruck wird der Ertrag etwas wie
Jedoch, dies nicht mit der Kardinalität von Klammern auf beiden Seiten. In anderen Worten,
{{{
entsprechen}
da Sie beide ungerade. Reguläre Ausdrücke können nicht zählen Dinge, so dass Sie nicht in der Lage sein, einen Ausdruck zu finden, entspricht der Kardinalität wie Sie wollen.Wirklich, was Sie tun sollten, ist das Parsen der strings mit einer benutzerdefinierten parser liest den string ein und zählt die Instanzen von
{
aber nicht Instanzen von{{
um Sie gegen Instanzen}
aber nicht}}
auf der anderen Seite. Ich glaube, du wirst finden, dies ist, wie String-Formatierer in .NET die Arbeit hinter den kulissen sowieso, wie reguläre Ausdrücke sind nicht geeignet für das Parsen von verschachtelten Strukturen jeglicher Art.Oder Sie können mit beiden Ideen in concert: Spiel-potential-Token mit einem regulären Ausdruck verwenden, dann überprüfen Sie Ihre Zahnspange Gleichgewicht mit einem quick-check auf die daraus resultierende match. Das würde wahrscheinlich am Ende als verwirrend und indirekten, aber dennoch. Sie sind in der Regel besser schreiben Sie Ihre eigenen parser für diese Art von Szenario.
Ich schließlich verwendet eine Technik ähnlich dem, was Gavin vorgeschlagen.
Änderte ich den regulären Ausdruck so, dass es passt alle Klammern um den Platzhalter :
Und ich änderte die Logik der
MatchEvaluator
so, dass es behandelt entkam Klammern richtig :Verlasse ich mich auf
String.Format
zu werfenFormatException
wenn nötig. Ich machte ein paar unit-tests, und bisher scheint es zu funktionieren...Danke an alle für Eure Hilfe !