Отображение строк в функциональность с помощью регулярных выражений - PullRequest
3 голосов
/ 26 ноября 2011

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

if (Regex.IsMatch(myString, regex1))
{
    //number
}
else if (Regex.IsMatch(myString, regex2))
{
    //something else
}
else if (Regex.IsMatch(myString, regex3))
{
    //something else
}

и так далее. Тем не менее, это выглядит очень неуклюже, так как мне нужно пройти через 10 регулярных выражений, так что я могу сделать то же самое, используя switch / case? Если да, могу ли я предоставить пример?

Я использую .NET 2.0 и WinForms.

Ответы [ 6 ]

2 голосов
/ 26 ноября 2011

Вы можете сохранить словарь ассоциаций - функцию сопоставления и логику обработки, затем просто циклически проходить совпадения и, как только он вернет true - выполнить связанную логику обработки, так что в итоге вы получите довольно простой цикл foreach:

string input = "some input ^&(*729384293";
string numberPattern = "regex to check numbers";
string datePattern = "regex to check date";

// KEY - match regex function
// VALUE - match handling logic
var matchesMap = new Dictionary<Func<bool>, Action>();

matchesMap.Add(
    () => Regex.IsMatch(input, numberPattern),
    () => { /*this code will be called when numer match found*/ });
matchesMap.Add(
    () => Regex.IsMatch(input, datePattern),
    () => { /*this code will be called when date match found*/ });

foreach (var match in matchesMap.Keys)
{
    if (match())
    {
        matchesMap[match].Invoke();
        break;
    }
}
1 голос
/ 27 ноября 2011

Это сводится к тому, сколько кода у вас есть для каждого случая Regex.

Если это всего лишь одна или две строки кода

Я говорю, придерживайтесь if-else.Он не только более прямолинеен и понятен, но и легче читается.


Если это сложно, или вы используете переключатель несколько раз

Если то, что у вас есть, довольно сложно, вы можете посмотреть на реализацию шаблона стратегии или что-то в этом роде.


Итог

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

1 голос
/ 27 ноября 2011

Одно «неуклюжее» решение - использовать упрощенную версию шаблона проектирования Chain Of Responsibility . Фактически, этот шаблон был создан для того, чтобы справляться с ситуациями, точно такими, как Дэвид описал здесь.

код

В вашем случае вам просто нужно сопоставить строки с действиями. Вы можете определить базовый класс, который выполняет итерацию по цепочке и выполняет заданное действие при обнаружении совпадения или перенаправляет ввод в следующую опцию.

abstract class Option
{
    public void DoActivity(string input)
    {
        Match parse = m_regex.Match(input);
        if (parse.Success)
            Activity(input);
        else if (m_nextOption != null)
            m_nextOption.DoActivity(input);
        else
            Console.WriteLine("No activity for input \"{0}\"", input);
    }

    // Each option defines its own activity; override as necessary.
    protected void Activity(string input)
    {
        Console.WriteLine(
            "For input \"{0}\" --> do activity defined in {1} ...", input, this);
    }

    // The next option in the chain, or null.
    protected Option m_nextOption;

    // Regular expression that matches the input for this option.
    protected Regex m_regex;
}

Затем создайте производные классы для столько вариантов, сколько вам нужно. Каждый определяет:

  1. регулярное выражение, соответствующее входам для обработки,
  2. действие, которое нужно выполнить для сопоставления входных данных (все используют метод базового класса в этом простом примере), и
  3. следующий вариант в цепочке, если он есть.

Что-то вроде:

internal class OptionOne : Option
{
    public OptionOne()
    {
        m_nextOption = new OptionTwo(); // next option.
        m_regex = new Regex(@"^Option One$", RegexOptions.Compiled);
    }
}
internal class OptionTwo : Option
{
    public OptionTwo()
    {
        m_nextOption = new OptionThree(); // next option.
        m_regex = new Regex(@"^Option Two$", RegexOptions.Compiled);
    }
 }
internal class OptionThree : Option
{
    public OptionThree()
    {
        m_nextOption = null; // no other options.
        m_regex = new Regex(@"^Option Three$", RegexOptions.Compiled);
    }
}

Использование

 // Possible input strings.
 string[] myStrings = { "Option One", "Option Two",
                        "Option Three", "Matches No Options" };

 // Do each input's activity ...
 Option options = new OptionOne();
 foreach (var myString in myStrings)
 {
     options.DoActivity(myString);
 }

выход

Для ввода «Вариант один» -> выполнить действие, определенное в StackOverflow.OptionOne ...
Для ввода «Вариант второй» -> выполнить действие, определенное в StackOverflow.OptionTwo ...
Для ввода «Вариант третий» -> выполнить действие, определенное в StackOverflow.OptionThree ...
Нет активности для ввода "Нет соответствий"

Еще одним преимуществом использования этого шаблона является то, что если вам необходимо добавить дополнительные параметры позже, вам просто нужно определить дополнительные производные классы и подключить их в цепочку. Код вызова не нужно менять вообще!

1 голос
/ 26 ноября 2011

Вы можете сделать более сложное регулярное выражение, например / (a) | (b) | (c) /, а затем проверить match.Groups впоследствии, какой шаблон соответствует:

Match match = Regex(input, @"/(a)|(b)|(c)/");
Foreach (Group g in match.Groups) {
  if (g.Success) {
    // Your code here
  }
}
1 голос
/ 26 ноября 2011

Единственный способ сделать это с помощью оператора switch-true (не рекомендуется).

switch (true)
{
    case Regex.IsMatch(myString, regex1):
        // ...
        break;
    case Regex.IsMatch(myString, regex2):
        // ...
        break;
    case Regex.IsMatch(myString, regex3):
        // ...
        break;
    default:
        // ...
        break;
}

EDIT: Бух Бух упомянул тот факт, что выражения case должны быть постоянными, поэтому код не будет компилироваться.

Есть еще один очень окольный способ решить эту проблему. Помните, я указываю на это не как рекомендацию, а как пример того, почему блок if лучше.

Если ваши регулярные выражения находятся в массиве regexes, вы можете сделать это:

int type = -1;
for (int i = 0; i < regexes.Length; i++)
{
    if (Regex.IsMatch(myString, regexes[i])
    {
        type = i;
        break;
    }
}
switch (type)
{
    case 0:
        // ...
        break;
    case 1:
        // ...
        break;
    case 2:
        // ...
        break;
    default:
        break;
}
1 голос
/ 26 ноября 2011

Это не может быть сделано, как вы описали, потому что switch может использоваться только с: bool, char string, int, enum или соответствующим обнуляемым типом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...