To find all¹ the palindromes of 3 or more characters not inside [...]
:
$ echo 'cac[ada]abacab' | perl -nle '
while (/\[.*?\]|(?=(([^][])(?1)\2|[^][]?))./g) {
print $1 if length $1 >= 3
}'
cac
aba
bacab
aca
(note that it assumes single-byte characters, add -Mopen=locale
for the locale definition of characters).
The core of the palindrome matching is a recursive regular expression. A palindrome is matched as either the empty string or a single character or a pair of matching characters with another palindrome in between. That would be ((.)(?1)\2|.?)
, where (?1)
does the recursive part (matches what's inside the first part of ()
, except that here we replace .
with [^][]
(any character other than ]
and [
).
When matching all occurrences with /.../g
, perl searches for the next occurrence after the end of the first one, so if we had \[.*?\]|(([^][])(?1)\2|[^][]?)
, we would not find bacab
in abacab
because it would first find aba
, and then resume searching after that aba
. So here instead, we match (?=(palindrome)).
which matches a single character (.
) provided it's at the start of a palindrome which is then captured in $1
. That means we resume searching after that single character.
¹ Strictly speaking, it finds the longest (of 3 characters or more) palindromes at every position in the string, skipping the [...]
s, so it may not find all the occurrences. For instance in ababa
, it would find ababa
in first position, bab
in third position, aba
in second position, but not aba
in first position.
perl -ne 'print if /(?<!\[)(.)(.)$2$1(?!\])/'
?(nothing)
would be a simplistic one;a
would be a simple one,aa
another...