计算文本文件中的回文



按照这个线程BASH在.txt文件中查找回文后,我不知道我的脚本做错了什么。

#!/bin/bash
search() {
tr -d '[[:punct:][:digit:]@]' 
| sed -E -e '/^(.)1+$/d'      
| tr -s '[[:space:]]'           
| tr '[[:space:]]' 'n'
}
search "$1"
paste <(search <"$1") <(search < "$1" | rev)     
| awk '$1 == $2 && (length($1) >=3) { print $1 }' 
| sort | uniq -c

我从这个脚本中得到的只是整个文本文件的输出。我只想输出回文>=3并计算它们,如

425做

120非

等等。我的文本文件名为sample.txt,每次我用:cat sample.txt | source回文运行脚本时,我都会收到消息"bash::No such file or directory"。

使用awksed

awk 'function palindrome(str) {len=length(str); for(k=1; k<=len/2+len%2; k++) { if(substr(str,k,1)!=substr(str,len+1-k,1)) return 0 } return 1 } {for(i=1; i<=NF; i++) {if(length($i)>=3){ gsub(/[^a-zA-Z]/,"",$i); if(length($i)>=3) {$i=tolower($i); if(palindrome($i)) arr[$i]++ }} } } END{for(i in arr) print arr[i],i}' file | sed -E '/^[0-9]+ (.)1+$/d'

1.2GB文件上测试,执行时间~4m 40s(i5-6440HQ @ 2.60GHz/4 cores/16GB)

解释:

awk '
function palindrome(str)               # Function to check Palindrome
{
len=length(str); 
for(k=1; k<=len/2+len%2; k++) 
{ 
if(substr(str,k,1)!=substr(str,len+1-k,1)) 
return 0 
} 
return 1 
} 
{
for(i=1; i<=NF; i++)               # For Each field in a record
{
if(length($i)>=3)              # if length>=3
{ 
gsub(/[^a-zA-Z]/,"",$i);   # remove non-alpha character from it
if(length($i)>=3)          # Check length again after removal
{
$i=tolower($i);        # Covert to lowercase
if(palindrome($i))     # Check if it's palindrome
arr[$i]++          # and store it in array
}
}
} 
} 
END{for(i in arr) print arr[i],i}' file | sed -E '/^[0-9]+ (.)1+$/d' 

sed -E '/^[0-9]+ (.)1+$/d':从最终结果中检查哪些字符串是由AAABBB等重复的字符组成的,并将其删除。


旧答案(编辑前)

如果你想,你可以尝试以下步骤:

步骤1:预处理
删除所有不必要的字符并将结果存储在临时文件中

tr -dc 'a-zA-Znt ' <file | tr ' ' 'n' > temp

tr -dc 'a-zA-Znt '这将删除除字母、nt、空格之外的所有字符

n这将把空间转换为grep -wof temp <(rev temp),以分隔换行中的每个字

步骤2:处理

grep -wof temp <(rev temp)  | sed -E -e '/^(.)1+$/d' | awk 'length>=3 {a[$1]++} END{ for(i in a) print a[i],i; }'

-w这将为您提供所有回文
level:只选择那些包含构成完整单词的匹配项的行。例如:levelAAA-o不匹配
-f:只打印匹配的组
temp:使用<(rev temp)文件中的每个字符串作为模式在sed -E -e '/^(.)1+$/d'中搜索

AAA:这将删除由相同字母组成的单词,如BBBBBawk 'length>=3 {a[$1]++} END{ for(i in a) print a[i],i; }'

length>=3:这将过滤具有$_的单词,并对其频率进行计数,最后打印结果

示例:

输入文件:

$ cat file
kayak nalayak bob dad , pikachu. meow !! bhow !! 121 545 ding dong AAA BBB done
kayak nalayak bob dad , pikachu. meow !! bhow !! 121 545 ding dong AAA BBB done
kayak nalayak bob dad , pikachu. meow !! bhow !! 121 545 ding dong AAA BBB done

输出:

$ tr -dc 'a-zA-Znt ' <file | tr ' ' 'n' > temp
$ grep -wof temp <(rev temp)  | sed -E -e '/^(.)1+$/d' | awk 'length>=3 {a[$1]++} END{ for(i in a) print a[i],i; }' 
3 dad
3 kayak
3 bob

只是一个快速的Perl替代方案:

perl -0nE 'for( /(w{3,})/g ){ $a{$_}++ if $_ eq reverse($_)}
END {say "$_ $a{$_}" for keys %a}'
  • 在Perl中,for( /(w{3,})/g )应读作">it">
  • if $_ eq reverse($_)。。。对于所有相关的单词(可能需要一些工作来拒绝像"12a21"这样的假阳性)
  • END {say "$_ $a{$_}" for...}。。。如果是回文
  • search "$1"。。。告诉我们所有itit的编号

\谢谢{sokowi,蝙蝠侠}

运行脚本

脚本要求将该文件作为参数给出。脚本不读取stdin。

删除脚本中间的chmod u+x path/to/palindrome行。这不是相关答案的一部分。

使用path/to/palindrome path/to/sample.txt使脚本可执行。

使用CCD_30调用脚本。如果所有文件都在当前工作目录中,则命令为

./palindrome sample.txt

备选脚本

链接的脚本有时有效,有时无效。我还不知道为什么。然而,我写了一个替代脚本,它也做了同样的事情,而且更干净:

#! /bin/bash
grep -Po 'w{3,}' "$1" |  grep -Evw '(.)1*' | sort > tmp-words
grep -Fwf <(rev tmp-words) tmp-words | uniq -c
rm tmp-words

保存脚本,使其可执行,并使用文件作为第一个参数来调用它。

最新更新