c # заменить пользовательские теги - PullRequest
0 голосов
/ 21 сентября 2011

У меня есть текстовый редактор, похожий на тот, который используется при переполнении стека.Я обрабатываю текстовую строку в c #, но также позволяю пользователям форматировать текст внутри нее с помощью пользовательского тега.Например ..

<year /> will output the current year.
"Hello <year /> World" would render Hello 2012 World

Я хотел бы создать регулярное выражение для поиска в строке любого вхождения <year /> и его замены.В дополнение к этому, я также хотел бы добавить атрибуты к тегу и иметь возможность извлекать их так <year offset="2" format="5" />.Я не очень хорошо с RegEx, но, надеюсь, кто-то там знает, как это сделать?

Спасибо

Ответы [ 2 ]

2 голосов
/ 21 сентября 2011

В идеале вы не должны использовать регулярные выражения для этого;но, учитывая, что Html Agility Pack не имеет HtmlReader Я думаю, что вам нужно.

При этом, рассматривая другие решения разметки, они часто используют список шаблонов регулярных выражений и соответствующую замену - такмы не должны писать «общий» случай (например, <([A-Z][A-Z0-9]*)>.*?</\1> было бы здесь неправильно, вместо этого мы бы хотели <year>.*?</year>).

Изначально вы, вероятно, создали бы класс для хранения информации ораспознанный токен, например:

public class Token
{
    private Dictionary<string, string> _attributes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    public string InnerText { get; private set; }

    public string this[string attributeName]
    {
        get
        {
            string val;
            _attributes.TryGetValue(attributeName, out val);
            return val;
        }
    }

    public Token(string innerText, IEnumerable<KeyValuePair<string, string>> values)
    {
        InnerText = innerText;
        foreach (var item in values)
        {
            _attributes.Add(item.Key, item.Value);
        }
    }

    public int GetInteger(string name, int defaultValue)
    {
        string val;
        int result;
        if (_attributes.TryGetValue(name, out val) && int.TryParse(val, out result))
            return result;
        return defaultValue;
    }
}

Теперь нам нужно создать регулярное выражение.Например, регулярное выражение, соответствующее вашему элементу year, будет выглядеть следующим образом:

<Year(?>\s*(?<aname>\w*?)\s*=\s*"(?<aval>[^"]*)"\s*)*>(?<itext>.*?)</Year>

Таким образом, мы можем обобщить это следующим образом:

<{0}\s*(?>(?<aname>\w*?)\s*=\s*"(?<aval>[^"]*)"\s*)*>(?<itext>.*?)</{0}>
<{0}\s*(?>(?<aname>\w*?)\s*=\s*"(?<aval>[^"]*)"\s*)*/>

Учитывая эти общие регулярные выражения тегов, мы можем написатькласс разметки:

public class MyMarkup
{
    // These are used to build up the regex.
    const string RegexInnerText = @"<{0}\s*(?>(?<aname>\w*?)\s*=\s*""(?<aval>[^""]*)""\s*)*>(?<itext>.*?)</{0}>";
    const string RegexNoInnerText = @"<{0}\s*(?>(?<aname>\w*?)\s*=\s*""(?<aval>[^""]*)""\s*)*/>";

    private static LinkedList<Tuple<Regex, MatchEvaluator>> _replacers = new LinkedList<Tuple<Regex, MatchEvaluator>>();

    static MyMarkup()
    {
        Register("year", false, tok =>
        {
            var count = tok.GetInteger("digits", 4);
            var yr = DateTime.Now.Year.ToString();
            if (yr.Length > count)
                yr = yr.Substring(yr.Length - count);
            return yr;
        });
    }

    private static void Register(string tagName, bool supportsInnerText, Func<Token, string> replacement)
    {
        var eval = CreateEvaluator(replacement);

        // Add the no inner text variant.
        _replacers.AddLast(Tuple.Create(CreateRegex(tagName, RegexNoInnerText), eval));
        // Add the inner text variant.
        if (supportsInnerText)
            _replacers.AddLast(Tuple.Create(CreateRegex(tagName, RegexInnerText), eval));
    }

    private static Regex CreateRegex(string tagName, string format)
    {
        return new Regex(string.Format(format, Regex.Escape(tagName)), RegexOptions.Compiled | RegexOptions.IgnoreCase);
    }

    public static string Execute(string input)
    {
        foreach (var replacer in _replacers)
            input = replacer.Item1.Replace(input, replacer.Item2);
        return input;
    }

    private static MatchEvaluator CreateEvaluator(Func<Token, string> replacement)
    {
        return match =>
        {
            // Grab the groups/values.
            var aname = match.Groups["aname"];
            var aval = match.Groups["aval"];
            var itext = match.Groups["itext"].Value;

            // Turn aname and aval into a KeyValuePair.
            var attrs = Enumerable.Range(0, aname.Captures.Count)
                .Select(i => new KeyValuePair<string, string>(aname.Captures[i].Value, aval.Captures[i].Value));

            return replacement(new Token(itext, attrs));
        };
    }
}

Это действительно грубая работа, но она должна дать вам хорошее представление о том, что вы должны делать.

0 голосов
/ 21 сентября 2011

string.Replace достаточно для первого требования - нет необходимости в RegEx.

string.Replace(myString, "<year />", @"<year offset=""2"" /">")

Чтобы извлечь значение атрибута - вы можете split на ":

var val = @"<year offset=""2"" /">".Split('"')[1];

Обновление (следующие комментарии):

Вы можете попробовать использовать Html Agility Pack для анализа и манипулирования текстом. Он хорошо работает с фрагментами HTML - хорошо и неправильно сформирован, хотя я не уверен, как он будет работать с пользовательскими тегами (стоит попробовать) Это может быть излишним.

...