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 😉
Schreibe einen Kommentar