由于awk正则表达式没有反向引用,因此纯awk实现会相当冗长,其用法简化了该方法。
I'ved加入一条线,为的多个最长单词的情况下的示例性输入文件:
1,helloguys.ca
2,byegirls.com
3,hellohelloboys.ca
4,hellobyebyedad.com
5,letswelcomewelcomeyou.org
6,letscomewelcomewelyou.org
这得到具有最长重复序列行:
cut -d ',' -f 2 infile | grep -Eo '(.*)\1' |
awk '{ print length(), $0 }' | sort -k 1,1 -nr |
awk 'NR==1 {prev=$1;print $2;next} $1==prev {print $2;next} {exit}' | grep -f - infile
由于这是非常明显的,让我们分开它做什么,并看看在每个阶段的输出:
删除与行号的第一列,以避免线路号码匹配与重复的数字:
$ cut -d ',' -f 2 infile
helloguys.ca
byegirls.com
hellohelloboys.ca
hellobyebyedad.com
letswelcomewelcomeyou.org
letscomewelcomewelyou.org
获取与重复序列中的所有行,仅提取该重复序列:
... | grep -Eo '(.*)\1'
ll
hellohello
ll
byebye
welcomewelcome
comewelcomewel
获取每个这些行的长度:
... | awk '{ print length(), $0 }'
2 ll
10 hellohello
2 ll
6 byebye
14 welcomewelcome
14 comewelcomewel
排序的第一列,数值,递减:
...| sort -k 1,1 -nr
14 welcomewelcome
14 comewelcomewel
10 hellohello
6 byebye
2 ll
2 ll
打印这些列,其中所述第一柱(长度)具有相同的值作为第一行上的所有线的第二:
... | awk 'NR==1{prev=$1;print $2;next} $1==prev{print $2;next} {exit}'
welcomewelcome
comewelcomewel
管到这一点的grep,使用-f -
参数读取标准输入作为一个文件:
... | grep -f - infile
5,letswelcomewelcomeyou.org
6,letscomewelcomewelyou.org
限制
虽然这可以处理意见中提到的bbwelcomewelcome
情况下,就会触发这种重叠图案作为welwelcomewelcome
,它只有找到welwel
,而不是welcomewelcome
。
更AWK替代解决方案,以下sort
如由评论tripleee指出的,这可以简化为跳过sort
步骤和组合两个AWK步骤和sort
步骤到单个AWK步骤,有可能提高性能:
$ cut -d ',' -f 2 infile | grep -Eo '(.*)\1' |
awk '{if (length()>ml) {ml=length(); delete a; i=1} if (length()>=ml){a[i++]=$0}}
END{for (i in a){print a[i]}}' |
grep -f - infile
让我们看一下详细awk会一步,扩大变量名称为清楚:
{
# New longest match: throw away stored longest matches, reset index
if (length() > max_len) {
max_len = length()
delete arr_longest
idx = 1
}
# Add line to longest matches
if (length() >= max_len)
arr_longest[idx++] = $0
}
# Print all the longest matches
END {
for (idx in arr_longest)
print arr_longest[idx]
}
标杆
我已经超时的top one million domains file两种解决方案在评论中提到:
第一溶液(用sort
和两个AWK步骤):
964438,abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com
real 1m55.742s
user 1m57.873s
sys 0m0.045s
第二种解决方案(只需一个awk步骤,不需要sort
):
964438,abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com
real 1m55.603s
user 1m56.514s
sys 0m0.045s
而且Perl solution通过Casimir et Hippolyte:
964438,abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com
real 0m5.249s
user 0m5.234s
sys 0m0.000s
我们从中学到了什么:问一个Perl的解决方案下一次;)
有趣的是,如果我们知道只会有一个最长的匹配,并相应地简化命令(只是head -1
而不是第一个解决方案的第二个awk命令在第二种解决方案中使用awk进行或不跟踪多个最长匹配),所获得的时间仅在几秒钟的范围内。
便携性的话
显然,BSD的grep不能做grep -f -
从标准输入读取。在这种情况下,管道的输出必须重定向到临时文件,然后使用此临时文件与grep -f
。
如果有超过一个最长的重复单词怎么办? (第一个,最后一个,都是?) –
我想他们都是! – cullan
第一个问题是如何找到一条记录中最长的重复子串,想象你有类似“bbwelcomewelcome”的东西,因为正则表达式引擎从左到右工作,基本模式会找到“bb”。 –