Pode expressões regulares ser usado para corresponder a padrões aninhadas? [duplicado]
-
02-07-2019 - |
Pergunta
Esta questão já tem uma resposta aqui:
É possível escrever uma expressão regular que corresponde a um padrão aninhado que ocorre um número desconhecido de vezes? Por exemplo, pode uma expressão regular corresponde a uma abertura e chave de fechamento quando há um número desconhecido de chaves de abertura / fecho aninhados dentro das chaves exteriores?
Por exemplo:
public MyMethod()
{
if (test)
{
// More { }
}
// More { }
} // End
Caso jogo:
{
if (test)
{
// More { }
}
// More { }
}
Solução
No. É tão fácil. Um autômato finito (que é a estrutura de dados subjacente uma expressão regular) não tem memória para além do estado em que está, e se você tiver arbitrariamente profunda aninhamento, você precisa de um arbitrariamente grande autômato, que colide com a noção de um finito autômato.
Você pode combinar aninhados / elementos emparelhados até uma profundidade fixa, onde a profundidade é limitado apenas pela sua memória, porque o autômato fica muito grande. Na prática, no entanto, você deve usar um para baixo impulso autômato, analisador ou seja um para uma gramática livre de contexto, por exemplo LL (top-down) ou LR (bottom-up). Tem de tomar o tempo de execução do comportamento pior em conta:. O (n ^ 3) vs S (n), com n = comprimento (entrada)
Existem muitos geradores de analisador avialable, por exemplo ANTLR para Java. Encontrar uma gramática existente para Java (ou C) também não é difícil.
Para mais fundo: Automata Teoria na Wikipedia
Outras dicas
Usando expressões regulares para verificar se há padrões aninhadas é muito fácil.
'/(\((?>[^()]+|(?1))*\))/'
solução Perl Provavelmente trabalhando, se a cadeia está em uma linha:
my $NesteD ;
$NesteD = qr/ \{( [^{}] | (??{ $NesteD }) )* \} /x ;
if ( $Stringy =~ m/\b( \w+$NesteD )/x ) {
print "Found: $1\n" ;
}
HTH
EDIT: de verificação:
- http://dev.perl.org/perl6/rfc/145.html
- informações rubi: http://www.ruby-forum.com/topic/112084
- mais perl: http://www.perlmonks.org/?node_id=660316
- ainda mais perl: https://metacpan.org/pod/Text::Balanced
- perl, perl, perl: http://perl.plover.com /yak/regex/samples/slide083.html
E mais uma coisa por Torsten Marek (que tinha apontado corretamente, que não é um regex mais) :
O lema do bombeamento para linguagens regulares é a razão pela qual você não pode fazer isso.
O autômato gerado terá um número finito de estados, dizem k, então uma seqüência de k + 1 chaves de abertura é obrigado a ter um lugar estado repetido (como o autômato processa os caracteres). A parte da cadeia de caracteres entre o mesmo estado pode ser repetido infinitamente muitas vezes e o autômato não vai saber a diferença.
Em particular, se ele aceita k + cintas uma abertura seguido de + 1 chaves de fechamento k (que deveria) que também irá aceitar o número de cintas bombeado abertura seguido por Brases k + 1 fechamento inalterados (o que não deveria ).
As expressões regulares adequada não seria capaz de fazê-lo como você iria deixar o reino das Linguagens Regulares à terra no contexto linguagens livres territórios.
Contudo os "expressão regular" pacotes que muitas línguas oferta são estritamente mais poderoso.
Por exemplo, Lua expressões regulares têm o reconhecedor "%b()
" que irá corresponder parêntese equilibrada. No seu caso, você usaria "%b{}
"
Outra ferramenta sofisticada semelhante ao sed é gema , onde irá partida equilibrada chaves muito facilmente com {#}
.
Assim, dependendo das ferramentas que você tem à sua disposição a sua "expressão regular" (em sentido amplo) pode ser capaz de corresponder parênteses aninhados.
Usando a correspondência recursiva no motor de regex PHP é maciçamente mais rápido do que a correspondência processual de suportes. especialmente com cadeias mais longas.
http://php.net/manual/en/regexp.reference.recursive.php
por exemplo.
$patt = '!\( (?: (?: (?>[^()]+) | (?R) )* ) \)!x';
preg_match_all( $patt, $str, $m );
vs.
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;
}
YES
... assumindo que existe algum número máximo de assentamentos que ficaria feliz de parar em.
Deixe-me explicar.
@ torsten-marek é certo que uma expressão regular não pode buscar por padrões aninhadas como este, MAS é possível definir um padrão regex aninhada que lhe permitirá capturar estruturas aninhadas como esta até uma profundidade máxima . Eu criei um para capturar EBNF-style comentários ( experimentá-lo aqui ), como:
(* This is a comment (* this is nested inside (* another level! *) hey *) yo *)
O regex (para comentários única de profundidade) é o seguinte:
m{1} = \(+\*+(?:[^*(]|(?:\*+[^)*])|(?:\(+[^*(]))*\*+\)+
Isto poderia facilmente ser adaptado para os seus fins, substituindo o \(+\*+
e \*+\)+
com {
e }
e substituindo tudo mais com um [^{}]
simples:
p{1} = \{(?:[^{}])*\}
( Aqui está o link para tentar isso.)
Para ninho, apenas permitir que este padrão dentro do próprio bloco:
p{2} = \{(?:(?:p{1})|(?:[^{}]))*\}
...or...
p{2} = \{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\}
Para encontrar blocos triple-aninhadas, use:
p{3} = \{(?:(?:p{2})|(?:[^{}]))*\}
...or...
p{3} = \{(?:(?:\{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\})|(?:[^{}]))*\}
Um claro padrão emergiu. Para encontrar aninhadas até uma profundidade de N
, basta usar o regex:
p{N} = \{(?:(?:p{N-1})|(?:[^{}]))*\}
where N > 1 and
p{1} = \{(?:[^{}])*\}
Um script poderia ser escrito para gerar recursivamente estas expressões regulares, mas isso está além do escopo do que eu preciso disso para. (Este é deixado como um exercício para o leitor. ??)
como zsolt mencionado, alguns motores de regex apoiar recursão - é claro, estes são normalmente os que utilizam um algoritmo de recuo por isso não vai ser particularmente eficiente. exemplo: /(?>[^{}]*){(?>[^{}]*)(?R)*(?>[^{}]*)}/sm
Não, você está entrando no reino da Contexto gramáticas livres nesse ponto .
Isso parece funcionar: /(\{(?:\{.*\}|[^\{])*\})/m