统计重复文本行的两种方法

February 6th, 2010 Categories: 应用

假设样本文件a.txt内容如下:

1
2
3
4
5
6
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程序,附注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/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程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/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! 被视为重复行。测试了一下,正则式没让我失望。

Tags:

7 Responses to “统计重复文本行的两种方法”

  1. lambdacpp
    February 7th, 2010 at 20:05
    1

    dup_re.pl 有点问题哦?
    $ cat e
    1
    12
    123
    1234
    12345

    $ cat e | ./rep_re.pl
    5times: 1
    4times: 2
    3times: 3
    2times: 4

    [Reply]

    rex Reply:

    谢谢提醒。已经修改。\1的两边加上了边界检查。

    [Reply]

    lambdacpp Reply:

    但是如果加了边界检测就匹配不了了:(

    [Reply]

    rex Reply:

    我记得测试过了呀~等我再测试一次。

    [Reply]

  2. Kid
    February 10th, 2010 at 15:03
    2

    Python版
    >>>d = {}
    >>>for c in open(r’data.txt’):
    d[c] = d.get(c, 0) + 1

    :D

    [Reply]

  3. February 10th, 2010 at 22:25
    3

    博主正解,哈希算法比較好,個人認為。

    [Reply]

    rex Reply:

    hash版的代码和思路看起来都比较简洁,算做是中规中矩的路子;regex的就有些geek范儿了。

    [Reply]

Leave a Comment