统计重复文本行的两种方法
假设样本文件a.txt内容如下:
hello world! hello world! I love regex. hello world! I love regex. hello world!
简单观察可知,hello world!共重复4行;I love regex.重复2行。如何使用正则表达式来写一个程序,统计这些数据呢?因为现实中需要统计的文件,绝非是只凭肉眼就能观察出来。我想到了两种方法,第一种方法,是依赖于正则表达式(否则这篇文章也不会贴在这里);第二种,hash表做主角,正则表达式作绿叶。
正则表达式的解法
思路是:对于任何一行文本,如果后面若干行[0~EOF)之后,如果存在相同的文本行,则记下该行内容,统计出现次数;然后删除这样的文本行,再进行下一行的统计。输出统计结果。
下面是相应的perl程序,附注释。
#!/usr/bin/perl #usage: ./dup_re.pl <a.txt undef $/; # enable "slurp" mode my $file = <STDIN>; # whole file now here while($file =~ m / #for each line; ^\s* #ignore the whitespaces at both ends; (\S.*?) #get the line content, save to $1; \s*$ #ignore empty lines by using \S .*? #check if there is the same pattern of $1 ^\s*\1\s*$ #after 0 or more lines; /smx) { my $line=$1; my $count= $file =~ s/$line//g; #delete the duplicated lines #save the number to $count; #ignore empty lines print $count,"times:\t",$line,"\n"; }
Hash表解法
这种方法,受益于perl语言本身的强大的hash表功能。思路如下:
- 建立空的hash表;
- 逐行读取文件;
- 以文本内容为key,插入到表中来。如果是首次出现,value为0,否则value++。
- 输出hash表中value>=2的记录。
Perl程序:
#!/usr/bin/perl #usage: ./dup_hash.pl a.txt my %hash=(); while(<>) { if (/^\s*(\S.*?)\s*$/) #ignore whitespaces at both ends; { #ignore empty lines by using \S $hash{$1}++; #save the line to $1, and count the time it appears } } #sort the hash by values; foreach $key (sort { $hash{$b} <=> $hash{$a} } keys %hash) { if ($hash{$key}>=2) #only print the lines that duplicates; { #for all results, just remove the 'if' line printf "%d times:\t%s\n", $hash{$key}, $key; } }
结果
上面的程序分别保存为dup_re.pl,dup_hash.pl。由于程序对于外部文件的读取的方法不同,运行方式也有差别,详见下图:

Update
忽然想到,如果要让这脚本更有效,可以指定忽略大小写,忽略单词间多个空格的情况,使得Hello world!与 hello WORLd! 被视为重复行。测试了一下,正则式没让我失望。
February 7, 2010 - 8:05 pm
dup_re.pl 有点问题哦?
$ cat e
1
12
123
1234
12345
$ cat e | ./rep_re.pl
5times: 1
4times: 2
3times: 3
2times: 4
February 8, 2010 - 9:30 am
谢谢提醒。已经修改。\1的两边加上了边界检查。
February 10, 2010 - 8:53 pm
但是如果加了边界检测就匹配不了了:(
February 11, 2010 - 12:37 am
我记得测试过了呀~等我再测试一次。
February 10, 2010 - 3:03 pm
Python版
>>>d = {}
>>>for c in open(r’data.txt’):
d[c] = d.get(c, 0) + 1
February 10, 2010 - 10:25 pm
博主正解,哈希算法比較好,個人認為。
February 11, 2010 - 12:36 am
hash版的代码和思路看起来都比较简洁,算做是中规中矩的路子;regex的就有些geek范儿了。