Para encontrar todos os¹ os palíndromos de 3 ou mais caracteres que não estão dentro de [...]
:
$ echo 'cac[ada]abacab' | perl -nle '
while (/\[.*?\]|(?=(([^][])(?1)|[^][]?))./g) {
print $1 if length $1 >= 3
}'
cac
aba
bacab
aca
(observe que ele assume caracteres de byte único, adicione -Mopen=locale
para a definição de caracteres do idioma).
O núcleo da correspondência do palíndromo é uma expressão regular recursiva. Um palíndromo é correspondido como uma string vazia ou um único caractere ou um par de caracteres correspondentes com outro palíndromo entre eles. Isso seria ((.)(?1)|.?)
, onde (?1)
faz a parte recursiva (corresponde ao que está dentro da primeira parte de ()
, exceto que aqui substituímos .
por [^][]
(qualquer caractere diferente de ]
e [
).
Ao corresponder todas ocorrências com /.../g
, o perl pesquisa a próxima ocorrência após o final da primeira, portanto, se tivéssemos \[.*?\]|(([^][])(?1)|[^][]?)
, não encontraríamos bacab
in abacab
porque primeiro encontraria aba
e, em seguida, retomaria a pesquisa após esse aba
. Então, aqui, em vez disso, combinamos (?=(palindrome)).
, que corresponde a um único caractere ( .
), desde que ele esteja no início de um palíndromo , que é então capturado em $1
. Isso significa que retomaremos a pesquisa após esse único caractere.
¹ Estritamente falando, ele encontra os palíndromes mais longos (com 3 caracteres ou mais) em todas as posições da string, ignorando o [...]
s, portanto, ele pode não encontrar todos os ocorrências. Por exemplo, em ababa
, encontraria ababa
na primeira posição, bab
na terceira posição, aba
na segunda posição, mas não aba
na primeira posição.