Можно ли использовать регулярные выражения для сопоставления вложенных шаблонов?



можно ли написать регулярное выражение, которое соответствует вложенному шаблону, который встречается неизвестное количество раз? Например, может ли регулярное выражение соответствовать открывающей и закрывающей фигурной скобке, когда существует неизвестное число открытых/закрытых фигурных скобок, вложенных во внешние фигурные скобки?



например:



public MyMethod()
{
if (test)
{
// More { }
}

// More { }
} // End


должны соответствовать:



{
if (test)
{
// More { }
}

// More { }
}
594   13  

13 ответов:

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

вы можете сопоставить вложенные / парные элементы до фиксированной глубины, где глубина ограничена только вашей памятью, потому что автомат становится очень большим. Однако на практике, вы должны использовать push-down автомат, т. е. парсер для контекстно-свободной грамматики, например LL (сверху вниз) или LR (снизу вверх). Вы должны учитывать худшее поведение во время выполнения: O(n^3) против O(n), с N = length (input).

есть много генераторов парсера avialable, например ANTLR для Java. Найти существующую грамматику для Java (или C) также не сложно.
Для получения дополнительной информации:Теория Автоматов в Википедии

использование регулярных выражений для проверки вложенных моделей очень легко.

'/(\((?>[^()]+|(?1))*\))/'

вероятно, работает решение Perl, если строка находится на одной строке:

my $NesteD ;
$NesteD = qr/ \{( [^{}] | (??{ $NesteD }) )* \} /x ;

if ( $Stringy =~ m/\b( \w+$NesteD )/x ) {
    print "Found: \n" ;
  }

HTH

EDIT: проверка:

и еще одна вещь, к Торстен Марек (кто правильно указал, что это больше не регулярное выражение):

да, если это .NET RegEx-engine. .Net engine поддерживает конечный автомат, поставляемый с внешним стеком. смотрите подробности

The Лемма накачки для регулярных языков почему вы не можете сделать это.

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

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

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

тем не менее пакеты" регулярных выражений", которые предлагают многие языки, являются строго более мощными.

например, Lua регулярные выражения есть "%b()" распознаватель, который будет соответствовать сбалансированной скобке. В вашем случае вы бы использовали "%b{}"

еще один сложный инструмент похож на sed-это гема, где вы будете соответствовать сбалансированному фигурные скобки очень легко с {#}.

Итак, в зависимости от инструментов, которые у вас есть в вашем распоряжении, ваше "регулярное выражение" (в более широком смысле) может соответствовать вложенной скобке.

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

http://php.net/manual/en/regexp.reference.recursive.php

например

$patt = '!\( (?: (?: (?>[^()]+) | (?R) )* ) \)!x';

preg_match_all( $patt, $str, $m );

и

matchBrackets( $str );

function matchBrackets ( $str, $offset = 0 ) {

    $matches = array();

    list( $opener, $closer ) = array( '(', ')' );

    // Return early if there's no match
    if ( false === ( $first_offset = strpos( $str, $opener, $offset ) ) ) {
        return $matches;
    }

    // Step through the string one character at a time storing offsets
    $paren_score = -1;
    $inside_paren = false;
    $match_start = 0;
    $offsets = array();

    for ( $index = $first_offset; $index < strlen( $str ); $index++ ) {
        $char = $str[ $index ];

        if ( $opener === $char ) {
            if ( ! $inside_paren ) {
                $paren_score = 1;
                $match_start = $index;
            }
            else {
                $paren_score++;
            }
            $inside_paren = true;
        }
        elseif ( $closer === $char ) {
            $paren_score--;
        }

        if ( 0 === $paren_score ) {
            $inside_paren = false;
            $paren_score = -1;
            $offsets[] = array( $match_start, $index + 1 );
        }
    }

    while ( $offset = array_shift( $offsets ) ) {

        list( $start, $finish ) = $offset;

        $match = substr( $str, $start, $finish - $start );
        $matches[] = $match;
    }

    return $matches;
}

как упоминал zsolt, некоторые механизмы регулярных выражений поддерживают рекурсию - конечно, это обычно те, которые используют алгоритм обратного отслеживания, поэтому он не будет особенно эффективным. пример: /(?>[^{}]*){(?>[^{}]*)(?R)*(?>[^{}]*)}/sm

нет, вы попадаете в царство Контекстно-Свободные Грамматики в этой точке.

да

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

позвольте мне объяснить.


@torsten-marek правильно, что регулярное выражение не может проверить вложенные шаблоны, как это, но можно определение вложенный шаблон регулярных выражений, который позволит вам захватить вложенные структуры, как это до некоторой максимальной глубины. Я создал один, чтобы захватить EBNF-style комментарии (попробуй здесь), как:

(* This is a comment (* this is nested inside (* another level! *) hey *) yo *)

регулярное выражение (для комментариев с одной глубиной) выглядит следующим образом:

m{1} = \(+\*+(?:[^*(]|(?:\*+[^)*])|(?:\(+[^*(]))*\*+\)+

это может быть легко адаптировано для ваших целей путем замены \(+\*+ и \*+\)+ С { и } и заменить все между ними с помощью простого [^{}]:

p{1} = \{(?:[^{}])*\}

(вот ссылка чтобы попробовать это.)

в гнездо, просто разрешить этот шаблон в самом блоке:

p{2} = \{(?:(?:p{1})|(?:[^{}]))*\}
  ...or...
p{2} = \{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\}

чтобы найти тройные вложенные блоки, используйте:

p{3} = \{(?:(?:p{2})|(?:[^{}]))*\}
  ...or...
p{3} = \{(?:(?:\{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\})|(?:[^{}]))*\}

появилась четкая картина. Чтобы найти комментарии, вложенные в глубину N, просто использовать регулярное выражение:

p{N} = \{(?:(?:p{N-1})|(?:[^{}]))*\}

  where N > 1 and
  p{1} = \{(?:[^{}])*\}

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

Это, кажется, работает: /(\{(?:\{.*\}|[^\{])*\})/m

мой вопрос+ответ связано, и я делаю выражение и мета-выражение, которое может соответствовать произвольным (конечным) уровням вложенности. Это довольно уродливо, но что еще можно ожидать? Используйте обратные ссылки в матче, если ваш движок поддерживает его.

нет. Вам нужен полноценный парсер для этого типа проблемы.

Comments

    Ничего не найдено.