Определение модели реализации регулярных выражений


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

Простой пример:

Сегодня {Сегодня}. Года {год}. В прошлом году был {год-1}.

Должна вывести что-то вроде:

Сегодня 26 января 2018. Года-2018. В прошлом году был 2017.

Есть еще несколько бизнес-и с учетом конкретных заполнителей и новые правила могут часто следовать.

Таким образом, я придумал этот класс:

public class PlaceholderDefinition
{
    public string Name { get; }
    public string Pattern { get; }
    private readonly Func<Match, string> _logic;

    public PlaceholderDefinition(string name, string pattern, Func<Match, string> logic)
    {
        Name = name;
        Pattern = pattern;
        _logic = logic;
    }

    public string Apply(Match match) => _logic.Invoke(match);
}

Для выполнения выше примера, зарегистрировать следующие PlaceholderDefinitions в службе конфигурации:

RegisteredPlaceholders.Add(
    new PlaceholderDefinition(
        "Today",
        @"{[tT][oO][dD][aA][yY]}",
        (_) => DateTime.Today.ToShortDateString()
    ));

RegisteredPlaceholders.Add(
    new PlaceholderDefinition(
        "Year",
        @"{[yY][eE][aA][rR]}",
        (_) => DateTime.Today.Year.ToString()
    ));

RegisteredPlaceholders.Add(
    new PlaceholderDefinition(
        "YearAddition",
        @"(?:{[yY][eE][aA][rR])([+-])(\d+)}",
        (m) =>
        {
            var operation = m.Groups[1].Value;
            int.TryParse(m.Groups[2].Value, out int value);
            return (operation == "+" ? DateTime.Today.Year + value : DateTime.Today.Year - value).ToString();
        }
    ));

В RegisteredPlaceholders это перечисляемое PlaceholderDefinition держали в контейнере.

Логику этих моделей/заполнителей применяется такой:

foreach (var placeholder in RegisteredPlaceholders)
 {
     var match = new Regex(placeholder.Pattern).Match(content);
     while (match.Success)
     {
         content = content.Remove(match.Index, match.Length).Insert(match.Index, placeholder.Apply(match));
         match = match.NextMatch();
     }
 }

Я решил использовать этот подход вместо использования Regex.Replace() чтобы не раскрыть содержание, или текст для класс, определяющий логику. Может быть, есть более элегантное решение, которое я не придумал.

Каждая критика, предложения по улучшению, запахи кода приветствуются.



324
4
задан 26 января 2018 в 08:01 Источник Поделиться
Комментарии
1 ответ


  • Попробуйте использовать именованные группы. Как ваш рисунок становится более сложным, он будет получать все труднее поддерживать.

    @"(?:{[yY][eE][aA][rR])([+-])(\d+)}"
    var operation = m.Groups[1].Value;
    int.TryParse(m.Groups[2].Value, out int value);

  • Реализация PlaceholderDefinition требует слишком много шаблонного кода, чтобы использовать:

     foreach (var placeholder in RegisteredPlaceholders)
    {
    var match = new Regex(placeholder.Pattern).Match(content);
    while (match.Success)
    {
    content = content.Remove(match.Index, match.Length).Insert(match.Index, placeholder.Apply(match));
    match = match.NextMatch();
    }
    }

    Это может быть сводились к: content = placeholder.Apply(text);


  • Нет необходимости использовать Match.NextMatch & string.Remove, когда вы можете использовать Regex.Replace

  • _logic кажется, плохо выбранное название. В то время как она описывает суть, но не его конкретная работа. Вы должны назвать Func С помощью "селектора, фабрики, строителя, ...", и префикс это с существительным ("результат, замена, ...") и отдельный член префикс _ (если это Конвенции, вы решили следовать).


Изменен PlaceholderDefinition класс:

public class PlaceholderDefinition
{
public string Name { get; }
public Regex Pattern { get; }

private readonly Func<GroupCollection, object> _replacementSelector;

public PlaceholderDefinition(string name, string pattern, Func<GroupCollection, object> replacementSelector)
: this(name, new Regex(pattern), replacementSelector)
{
}
public PlaceholderDefinition(string name, string pattern, RegexOptions options, Func<GroupCollection, object> replacementSelector)
: this(name, new Regex(pattern, options), replacementSelector)
{
}
public PlaceholderDefinition(string name, Regex pattern, Func<GroupCollection, object> replacementSelector)
{
this.Name = name;
this.Pattern = pattern;
this._replacementSelector = replacementSelector;
}

public string Apply(string input) => Pattern.Replace(input, m => _replacementSelector(m.Groups).ToString());
}

Есть различные перегрузки ctor для удовлетворения различных потребностей, как предложил @t3chb0t. Чувствует себя свободно, чтобы добавить больше, если это поможет вам сохранить деклараций как можно более чистым.

И, остальной код:

var text = "Today is {today}. The year is {year}. Last year was {year-1}.";
var placeholders = new List<PlaceholderDefinition>();
placeholders.Add(new PlaceholderDefinition("Today", @"{today}", RegexOptions.IgnoreCase, _ => DateTime.Today.ToShortDateString()));
placeholders.Add(new PlaceholderDefinition("Year", @"{year}", RegexOptions.IgnoreCase, _ => DateTime.Today.Year));
placeholders.Add(new PlaceholderDefinition(
"YearAddition",
@"{year(?<sign>[+-])(?<value>\d+)}",
RegexOptions.IgnoreCase,
g => DateTime.Today.Year + int.Parse(g["sign"].Value + g["value"].Value)
));

foreach (var placeholder in placeholders)
{
text = placeholder.Apply(text);
}



  • В _replacementSelector принимает в GroupCollection, так что мы можем не писать m => m.Group...и object становится TResult позволяет нам пропустить необходимые .ToString() или скобочки на бинарной операции.

  • int.Parse анализирует количество предваряется + знак как положительное число, что позволило нам пропустить тернарную операцию.

3
ответ дан 26 января 2018 в 10:01 Источник Поделиться