Соответствие сбалансированным скобкам в Ruby с использованием рекурсивных регулярных выражений, таких как perl - PullRequest
13 голосов
/ 13 июня 2011

Я искал способ сопоставить сбалансированные скобки в регулярном выражении и нашел способ в Perl, который использует рекурсивное регулярное выражение:

my $re;
$re = qr{
           \(
              (?:
                 (?> [^()]+ )       # Non-parens without backtracking
                 |
                 (??{ $re })        # Group with matching parens
              )*
           \)
         }x;

с сайта регулярного выражения perl .

Есть ли способ сделать это на Ruby или аналогичном языке?

UPDATE :

Для интересующихся вот несколько интересных ссылок:

Руководство по Онигуруме - из ответа Саввы.

Пример регулярных выражений Ruby 1.9 для прагматичных программистов Глава

Ответы [ 2 ]

20 голосов
/ 13 июня 2011

Да. Вы можете сделать это с помощью oniguruma regex engine, который встроен в Ruby 1.9 и устанавливается на Ruby 1.8. Вы называете subregex с (?<name>...) или (?'name'...). Затем вы вызываете под-выражение с \g<name> или \g'name' в том же регулярном выражении. Таким образом, ваше регулярное выражение, переведенное в регулярное выражение oniguruma, будет:

re = %r{
  (?<re>
    \(
      (?:
        (?> [^()]+ )
        |
        \g<re>
      )*
    \)
  )
}x

Также обратите внимание, что в многобайтовом модуле строк в PHP> = 5 используется механизм регулярных выражений oniguruma, поэтому вы сможете сделать то же самое.

Руководство по онигуруме здесь .

0 голосов
/ 18 июля 2015

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

ESC= /(?<![\\])(?>[\\](?:[\\][\\])*)/
UNESC= /(?:\A|(?<=[^\\]))(?:[\\][\\])*/
BALANCED_PARENS = /#{UNESC}(
                   (?<bal>\(
                    (?>
                      (?>  (?:#{ESC}\(|#{ESC}\)|[^()])+     )
                      |\g<bal>
                    )*
                    \))    ) /xm

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

Причиной сложности ESC и UNESC является предположение о том, что \\ является экранированной обратной косой чертой.Мы используем только последовательность UNESC до начального совпадения, так как любые другие экранированные скобки будут сопоставлены внутри атомарной группы и никогда не будут возвращены.В самом деле, если бы мы попытались использовать префикс UNESC для внутреннего или окончательного совпадения, то произойдет сбой, когда [^ ()] внутри атомарной группы совпадет с ведущими и откажется вернуться назад.

Это регулярное выражениеотсканирует первое имя, которое ограничивает правильную сбалансированную скобку.Таким образом, учитывая строку "((stuff)", она будет соответствовать "(stuff)". Часто желаемое поведение - найти первую (неэкранированную) скобку и либо соответствовать внутренней (если сбалансирована), либо не соответствовать. К сожалению,атомарная группировка не остановит резервирование всего регулярного выражения и попытку сопоставления на более позднем этапе, поэтому мы должны закрепиться в начале строки и посмотреть только на первый захват. Следующее регулярное выражение делает это изменение:

BALANCED_PARENS = /\A(?:#{ESC}\(|#{ESC}\)|[^()])*+
                  (?<match>\(
                   (?<bal>
                    (?>
                      (?>  (?:#{ESC}\(|#{ESC}\)|[^()])+     )
                      |\(\g<bal>
                    )*
                    \))    ) /xm
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...