我有一个包含一堆字符串的文件,每行一个。我只想查找并打印一个字符串,前提是它包含回文并且该回文不在括号之间。例子:
abba[cdef]gh # print
abcd[effe]gh # do not print
我目前有一个单行代码,如果字符串是回文,它会打印一行
awk 'BEGIN { system("perl -lne \"print if length == 4 && reverse eq \\$_\" " ARGV[1]) }' words.txt
修改自这里。
我正在考虑使用sed
删除括号之间的所有内容,然后评估剩余的回文。
关于如何在一行中完成此任务还有其他想法吗?
答案1
要查找不在 内的所有 1 个 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
in,abacab
因为它会首先找到aba
,然后在该之后继续搜索aba
。因此,在这里,我们匹配(?=(palindrome)).
匹配单个字符 ( .
) 的条件,前提是它位于一个字符的开头回文然后被捕获在$1
.这意味着我们继续搜索该单个字符。
严格来说,它会在字符串中的每个位置查找最长的(3 个字符或更多)回文,跳过 s [...]
,因此可能找不到全部的事件。例如,在 中ababa
,它会ababa
在第一个位置、bab
第三个位置、aba
第二个位置找到,但不会aba
在第一个位置找到。
答案2
说好的单线呢?使用括号内的单词作为字段分隔符:
perl -F'\[.*?\]' -le 'for $word (@F) {if ($word eq reverse $word) {print; break}}' file
这里没有考虑一些边缘情况:
- 它不考虑字符串长度
- 它看起来没有找到回文数之内单词:整个单词必须是回文。
答案3
虽然[...]
可以提前过滤掉,但使用类似 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
%
这可以很容易地适应忽略尾随注释或其他此类输入。