Как я могу указать приоритет шаблона соответствия в регулярном выражении? - PullRequest
7 голосов
/ 13 декабря 2010

Я пишу механизм синтаксического анализа функций, который использует регулярные выражения для разделения отдельных терминов (определенных как константа или переменная, за которой (необязательно) следует оператор). Это прекрасно работает, за исключением случаев, когда я сгруппировал термины в другие сгруппированные термины. Вот код, который я использую:

//This matches an opening delimiter
Regex openers = new Regex("[\\[\\{\\(]");

//This matches a closing delimiter
Regex closers = new Regex("[\\]\\}\\)]");

//This matches the name of a variable (\w+) or a constant numeric value (\d+(\.\d+)?)
Regex VariableOrConstant = new Regex("((\\d+(\\.\\d+)?)|\\w+)" + FunctionTerm.opRegex + "?");

//This matches the binary operators +, *, -, or /
Regex ops = new Regex("[\\*\\+\\-/]");

//This compound Regex finds a single variable or constant term (including a proceeding operator,
//if any) OR a group containing multiple terms (and their proceeding operators, if any)
//and a proceeding operator, if any.
//Matches that match this second pattern need to be added to the function as sub-functions,
//not as individual terms, to ensure the correct evalutation order with parentheses.
Regex splitter = new Regex(
openers + 
"(" + VariableOrConstant + ")+" + closers + ops + "?" +
"|" +
"(" + VariableOrConstant + ")" + ops + "?");

Когда "splitter" сопоставляется со строкой "4 / (2 * X * [2 + 1])", значениями совпадений являются "4 /", "2 *", "X *", "2 + "и" 1 ", полностью игнорируя все разделительные скобки и скобки. Я полагаю, что это потому, что вторая половина регулярного выражения "сплиттер" (часть после "|") сопоставляется и переопределяет другую часть шаблона. Это плохо - я хочу, чтобы сгруппированные выражения имели приоритет над отдельными терминами. Кто-нибудь знает, как я могу это сделать? Я изучал использование положительных / отрицательных взглядов и взглядов назад, но я, честно говоря, не уверен, как их использовать, или для чего они вообще, и я не могу найти соответствующие примеры ... Заранее спасибо .

Ответы [ 2 ]

5 голосов
/ 14 декабря 2010

Вы не показали нам, как вы применяете регулярное выражение, поэтому вот демонстрация, которую я взбил:

private static void ParseIt(string subject)
{
  Console.WriteLine("subject : {0}\n", subject);

  Regex openers = new Regex(@"[[{(]");
  Regex closers = new Regex(@"[]})]");
  Regex ops = new Regex(@"[*+/-]");
  Regex VariableOrConstant = new Regex(@"((\d+(\.\d+)?)|\w+)" + ops + "?");

  Regex splitter = new Regex(
    openers + @"(?<FIRST>" + VariableOrConstant + @")+" + closers + ops + @"?" +
    @"|" +
    @"(?<SECOND>" + VariableOrConstant + @")" + ops + @"?",
    RegexOptions.ExplicitCapture
  );

  foreach (Match m in splitter.Matches(subject))
  {
    foreach (string s in splitter.GetGroupNames())
    {
      Console.WriteLine("group {0,-8}: {1}", s, m.Groups[s]);
    }
    Console.WriteLine();
  }
}

выход:

subject : 4/(2*X*[2+1])

group 0       : 4/
group FIRST   :
group SECOND  : 4/

group 0       : 2*
group FIRST   :
group SECOND  : 2*

group 0       : X*
group FIRST   :
group SECOND  : X*

group 0       : [2+1]
group FIRST   : 1
group SECOND  :

Как видите, термин [2+1] равен , совпадающему с первой частью регулярного выражения, как вы и предполагали. Тем не менее, он ничего не может сделать с (, потому что следующий символ скобки после него - это еще один «открывающий» ([), и он ищет «ближе».

Вы могли бы использовать функцию "сбалансированного соответствия" .NET, чтобы сгруппировать термины, включенные в другие группы, но это не стоит усилий. Регулярные выражения не предназначены для синтаксического анализа - фактически синтаксический анализ и сопоставление регулярных выражений являются принципиально различными видами операций. И это хороший пример различия: регулярное выражение активно ищет совпадения, пропуская все, что не может использовать (например, открывающую скобку в вашем примере), но парсер должен исследовать каждый символ (даже если он просто решите проигнорировать это).

О демо: я попытался сделать минимальные функциональные изменения, необходимые для работы вашего кода (именно поэтому я не исправил ошибку при установке + снаружи группа захвата), но я также внес несколько изменений поверхность , и они представляют собой активные рекомендации. Для остроумия:

  • Всегда используйте дословные строковые литералы (@"...") при создании регулярных выражений в C # (я думаю, причина очевидна).
  • Если вы используете группы захвата, по возможности используйте именованные группы, но не используйте именованные группы и нумерованные группы в одном и том же регулярном выражении. Именованные группы избавляют вас от необходимости отслеживать, что и где захвачено, а опция ExplicitCapture избавляет вас от необходимости загромождать регулярное выражение с (?:...) везде, где вам нужна группа без захвата.

Наконец, вся эта схема построения большого регулярного выражения из группы меньших регулярных выражений имеет очень ограниченную полезность IMO. Очень трудно отслеживать взаимодействия между частями, например, какая часть внутри какой группы. Еще одно преимущество стенографических строк C # состоит в том, что они многострочные, поэтому вы можете использовать режим свободного пробела (a.k.a. /x или режим КОММЕНТАРИЙ):

  Regex r = new Regex(@"
    (?<GROUPED>
      [[{(]                  # opening bracket
      (                      # group containing:
        ((\d+(\.\d+)?)|\w+)     # number or variable
        [*+/-]?                 # and proceeding operator
      )+                     # ...one or more times
      []})]                  # closing bracket
      [*+/-]?                # and proceeding operator
    )
    |
    (?<UNGROUPED>
      ((\d+(\.\d+)?)|\w+)    # number or variable
      [*+/-]?                # and proceeding operator
    )
    ",
    RegexOptions.ExplicitCapture | RegexOptions.IgnorePatternWhitespace
  );

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

2 голосов
/ 13 декабря 2010

попробуйте использовать разные квантификаторы

жадный:

*  +  ?

притяжательные:

*+ ++ ?+

ленивый:

*? +? ??

Попробуйте прочитать это и это

также может быть группа без захвата:

(?:your expr here)

попробуй попробуй попробуй! практика создает совершенство! :)

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