【发布时间】:2012-03-06 12:08:25
【问题描述】:
我想对一个文件(大约 200 兆字节)进行模式匹配,然后将匹配行以及每个匹配行之前和之后的任意行数推入一个数组。
sub1,使用 perl grep,耗时 11 秒
sub2,使用unix egrep,1秒
sub6 (ack) 50 秒(如果你不使用 \b、\s 锚等会更快)
从命令行确认需要 15 秒
我对加速 sub1 的建议感兴趣,或者寻找不依赖外部工具的快速 perl 解决方案
perl grep 似乎比 unix 慢很多。
"index" 确实比正则表达式快(但我需要 \b、\s 等)
http://www.perlmonks.org/?node_id=885174
http://www.perlmonks.org/?node_id=957554
谢谢
use 5.014;
use strict;
use warnings;
use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);
use List::MoreUtils qw(uniq);
open FILE, '<textMatchInAfile.txt' or die;
my $p = '\bsala|che|relazione|di|questo|coso|^qui\$';
my $mR = 1; #print more rows before - after the matching
my @n = <FILE>;
&sub1( $p, $mR, @n ); #suggest: insert references
&sub3( $p, $mR );
sub sub1 { #questa sub usa perl grep
my $p = $_[0]; #pattern
my $mR = $_[1]; #more rows
my @n = @_[ 2 .. $#_ ]; #input File
my $time = [gettimeofday];
my @new = grep { $n[$_] =~ /$p/ } 0 .. $#n;
my @unique =
map { @n[ $_ - $mR .. $_ + $mR ] } @new[ 0 + $mR .. $#new - $mR];
say "\n" . 'time sub1 perl grep: ' . tv_interval($time);
@unique = uniq(@unique);
say "sub 1 $#unique";
}
sub sub3 { #unix grep with color and line numbers
my $p = $_[0];
my $mR = $_[1];
my $cmd = "grep -n -C $mR"; #with line numbers
$p =~ s/\|/ /g;
$p =~ s/\h+/" -e "/g;
$p = ' -e "' . $p . '" ';
say "cmd ===$cmd=== ss ===$p===";
my @values;
$values[0] = $p;
$values[1] = ( ' ' . 'textMatchInAfile.txt' );
my $time = [gettimeofday];
my @valori = `$cmd @values` or die "system @values` failed: $?";
say 'sub3 egrep shell: ' . $#valori;
say 'time sub3 tempo trovati con egrep shell ' . tv_interval($time);
my @uniq_list = uniq(@valori);
}
sub sub6 { #perl ack
my $p = $_[0]; #pattern
my $mR = $_[1]; #more rows
my @values;
my $time = [gettimeofday];
my @valori = qx (ack -C $mR "$p" textMatchInAfile.txt)
or die "system @values` failed: $?";
say 'number of values found with ack' . $#valori;
say 'time sub6 ack' . tv_interval($time);
}
#
#this one takes 11 seconds
use 5.014;
use warnings;
use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);
my @array;
my $pattern = '\bsala|che|relazione|di|questo|coso|^qui\$';
open( my $filehandle, "<textMatchInAfile.txt" );
my $time = [gettimeofday];
while (<$filehandle>) {
if ( $_ =~ /$pattern/ ) {
push @array;
}
}
say 'time while' . tv_interval($time);
好吧好吧,unix grep 比 perl grep 快一个数量级,我会接受的。
【问题讨论】:
-
我推荐使用Benchmark 模块。我发现您的代码有点难以阅读,并且很难编写自己的代码,因为我需要一些输入数据的样本。但据我所知,您正在将文件的全部内容加载到数组中。最好做一个
while循环并逐行处理文件。毕竟 Perl 应该几乎与带有管道的 unix 工具一样快编译后,这可能需要一些时间(对于大量输入数据来说微不足道)。 -
我的输入数据只是普通的文本散文。我发现没有办法与 unix grep 的速度竞争,我想我已经尝试了所有可能的方法
-
好吧,我尝试用一些数据编写自己的子程序来解决这个问题(只是为了好玩),但这是不可能的,因为我绝对无法理解您的代码实际上在做什么。考虑到您的示例潜艇的质量和效率,我很确定您的代码仍有很大的优化空间。但是除非你提供一些数据并描述你真正想用它做什么,否则没人会告诉你。
-
谢谢,我确定没有太多优化空间,使用 unix grep 的 sub 总是快 10 倍左右,我尝试了很多不同的解决方案。