Чтобы найти все¹ палиндромы из 3 и более символов не внутри[...]
:
$ echo 'cac[ada]abacab' | perl -nle '
while (/\[.*?\]|(?=(([^][])(?1)\2|[^][]?))./g) {
print $1 if length $1 >= 3
}'
cac
aba
bacab
aca
(обратите внимание, что он предполагает использование одиночных -байтовых символов, добавьте -Mopen=locale
для определения локали символов ).
Ядром сопоставления палиндрома является рекурсивное регулярное выражение. Палиндром сопоставляется либо как пустая строка, либо как один символ, либо как пара совпадающих символов с другим палиндромом между ними. Это будет ((.)(?1)\2|.?)
, где (?1)
рекурсивная часть (соответствует тому, что находится внутри первой части ()
, за исключением того, что здесь мы заменяем .
на[^][]
(любой символ, кроме ]
и [
).
При сопоставлении всех вхождений с /.../g
Perl ищет следующее вхождение после конца первого, поэтому, если бы у нас было \[.*?\]|(([^][])(?1)\2|[^][]?)
, мы бы не нашли bacab
в abacab
. ] потому что он сначала найдет aba
, а затем возобновит поиск после этого aba
. Поэтому здесь вместо этого мы сопоставляем (?=(palindrome)).
, который соответствует одному символу (.
), при условии, что он находится в начале палиндрома , который затем фиксируется в $1
. Это означает, что мы продолжаем поиск после этого единственного символа.
¹ Строго говоря, он находит самый длинный (из 3 или более символов )палиндромов в каждой позиции строки, пропуская [...]
s, поэтому он может не найти все палиндромы. происшествия. Например, в ababa
будет найдено ababa
в первой позиции, bab
в третьей позиции, aba
во второй позиции, но не aba
в первой позиции.
Хотя [...]
можно было бы отфильтровать заранее, может быть более разумным использовать lex -, подобный сканеру , для обхода линии:
#!/usr/bin/env perl
use strict;
use warnings;
LINE: while (readline) { # for each line (files or stdin)
LEX: {
# skip any [] or [...] bits
redo LEX if m{ \G \[ [^\]]* \] }cgx;
# two or more not-[ not-vertical-whitespace (\r, \n) chars
if (m{ \G ([^\[\v]{2,}) }cgx) {
# palindrome? print the whole line
if ( $1 eq reverse $1 ) {
print;
next LINE;
}
# may be more to come...
redo LEX;
}
# advance the lexer a single character
redo LEX if m{ \G. }cgx;
# oh it's the end of the line as we know it
}
}
Который с расширенным набором тестов для различных граничных условий:
% < input
abba[cdef]gh # print
abcd[effe]gh # do not print
[effe]f00f
asdf[]prinirp
a[]b[]edgegde
% perl palin < input
abba[cdef]gh # print
[effe]f00f
asdf[]prinirp
a[]b[]edgegde
%
Это можно легко адаптировать для игнорирования завершающих комментариев или других подобных входных данных.
Как насчет одного -вкладыша? Используйте слово в квадратных скобках в качестве разделителя полей:
perl -F'\[.*?\]' -le 'for $word (@F) {if ($word eq reverse $word) {print; break}}' file
Пара крайних случаев, здесь не рассмотренных: