在文本文件中计算回文

时间:2017-10-21 16:18:49

标签: bash ubuntu command-line palindrome

跟着这个帖子BASH Finding palindromes in a .txt file我无法弄清楚我的脚本出了什么问题。

#!/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

从这个脚本获取的所有内容都是整个文本文件的输出。我只想输出palindromes&gt; = 3并计算它们,如

425

120非

等。我的文本文件名为sample.txt,每次运行脚本时都会使用:cat sample.txt | source palindrome我得到消息'bash ::没有这样的文件或目录'。

3 个答案:

答案 0 :(得分:3)

使用 awk sed

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-Z\n\t ' <file | tr ' ' '\n' > temp

tr -dc 'a-zA-Z\n\t '这将删除除\n\t,空格

以外的所有字母

tr ' ' '\n'这会将空格转换为\n以分隔换行符中的每个单词

第二步:处理

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

grep -wof temp <(rev temp)这会给你所有的回文 -w:仅选择包含构成整个单词的匹配项的行。 例如:levellevelAAA不匹配 -o:仅打印匹配的组
-f:要将temp文件中的每个字符串用作模式,以便在<(rev temp)中搜索

sed -E -e '/^(.)\1+$/d':这会删除由AAABBBBB

等相同字母组成的字词

awk '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-Z\n\t ' <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

答案 1 :(得分:3)

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

perl -0nE 'for( /(\w{3,})/g ){ $a{$_}++ if $_ eq reverse($_)}
           END {say "$_ $a{$_}" for keys %a}'
    Perl中的
  • $_应该被理解为“ it ”。
  • for( /(\w{3,})/g ) ...所有相关词汇(可能需要一些工作来拒绝像“12a21”这样的误报)
  • if $_ eq reverse($_) ...如果是回文
  • END {say "$_ $a{$_}" for...} ...告诉我们所有的号码

\ {感谢sokowi,蝙蝠侠}

答案 2 :(得分:2)

运行脚本

脚本期望该文件作为参数提供。该脚本不读取标准输入。

删除脚本中间的行search "$1"。它不是链接答案的一部分。

使用chmod u+x path/to/palindrome使脚本可执行。

使用path/to/palindrome path/to/sample.txt调用脚本。如果所有文件都在当前工作目录中,则命令为

./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

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