我有一个大的平面文本文件,其中的行包含名称/值对("varname = value").这些对由多字符分隔符分隔.因此,此文件中的单行可能如下所示:
var1=value1|^|var2=value2|^|var3=value3|^|var4=value4
Run Code Online (Sandbox Code Playgroud)
每行包含大约50个名称/值对.
我需要迭代这个文件的行(大约有100,000行)并将名称/值对存储在一个哈希中,这样
$field{'var1'} = value1
$field{'var2'} = value2
etc...
Run Code Online (Sandbox Code Playgroud)
我做的是这样的:
# $line holds a single line from the file
my @fields = split( /\Q|^|\E/, $line );
foreach my $field (@fields) {
my ($name, $value) = split( /=/, $field );
$hash{$name} = $value;
}
Run Code Online (Sandbox Code Playgroud)
对整个文件的每一行执行此操作需要(在我的PC上)大约2秒钟.这似乎不是很长时间,但我真的想加快这一点.
在这2秒中,第一次分割大约需要0.6秒,而foreach循环大约需要1.4秒.所以我以为我会摆脱foreach循环并把它全部放在一个分裂中:
%hash = split( /\Q|^|\E|=/, $line );
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,以这种方式解析整个文件需要更长的时间!我的问题并不是为什么这需要更长时间(虽然理解为什么会是一个很好的奖励),但我的问题是,是否还有其他(更快)的方法来完成工作.
提前致谢.
------编辑下面这行------
我发现改变了这个:
%hash = split( /\Q|^|\E|=/, $line );
Run Code Online (Sandbox Code Playgroud)
进入这个:
$line =~ s/\Q|^|\E/=/g;
%hash = split( /=/, $line );
Run Code Online (Sandbox Code Playgroud)
使它快三倍!以这种方式解析整个文件现在需要一秒钟......
------这条线下面的片段------
use strict;
use Time::HiRes qw( time );
my $line = "a=1|^|b=2|^|c=3|^|d=4|^|e=5|^|f=6|^|g=7|^|h=8|^|i=9|^|j=10|^|k=11|^|l=12|^|m=13|^|n=14|^|o=15|^|p=16|^|q=17|^|r=18|^|s=19|^|t=20|^|u=21|^|v=22|^|w=23|^|x=24|^|y=25|^|z=26|^|aa=27|^|ab=28|^|ac=29|^|ad=30|^|ae=31|^|af=32|^|ag=33|^|ah=34|^|ai=35|^|aj=36|^|ak=37|^|al=38|^|am=39|^|an=40|^|ao=41|^|ap=42|^|aq=43|^|ar=44|^|as=45|^|at=46|^|au=47|^|av=48|^|aw=49|^|ax=50";
ResetTimer();
my %hash;
for( my $i = 1; $i <= 100000; $i++ ) {
my @fields = split( /\Q|^|\E/, $line );
foreach my $field (@fields) {
my ($name, $value) = split( /=/, $field );
$hash{$name} = $value;
}
}
print Elapsed() . "\n";
ResetTimer();
%hash = ();
for( my $i = 1; $i <= 100000; $i++ ) {
%hash = split( /\Q|^|\E|=/, $line );
}
print Elapsed() . "\n";
ResetTimer();
%hash = ();
for( my $i = 1; $i<=100000; $i++ ) {
$line =~ s/\Q|^|\E/=/g;
%hash = split( /=/, $line );
}
print Elapsed() . "\n";
################################################################################################################################
BEGIN {
my $startTime;
sub ResetTimer {
$startTime = time();
return $startTime;
}
sub Elapsed {
return time() - $startTime;
}
}
Run Code Online (Sandbox Code Playgroud)
我无法轻易回答您的性能问题,因为我需要一个测试用例.但我猜这与正则表达式的处理方式有关.
你可以看到它正在做什么use re 'debug';,并打印正则表达式步骤.
但对于更广泛的问题 - 我可能只是用全局解决它(假设您的数据就像示例一样简单):
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
while ( <DATA> ) {
my %row = m/(\w+)=(\w+)/g;
print Dumper \%row;
}
__DATA__
var1=value1|^|var2=value2|^|var3=value3|^|var4=value4
Run Code Online (Sandbox Code Playgroud)
你可以使用lookahead/behind匹配分隔符,如果你有更复杂的东西,但因为它是每行一个正则表达式,你不太经常调用正则表达式引擎,而且可能会更快.(但是如果没有测试用例,我无法确定).
如果你的数据是比较复杂的话,或许:
my %row = s/\Q|^|\E/\n/rg =~ m/(.*)=(.*)/g;
Run Code Online (Sandbox Code Playgroud)
这将"强制"将输入拆分为新行,然后匹配'anything'='nothing'.但除非您的值包括空格/管道/元数据,否则这可能是过度的.
编辑您要使用的测试用例Benchmark:
#!/usr/bin/env perl
use strict;
use warnings;
use Benchmark qw ( cmpthese );
my $line =
"a=1|^|b=2|^|c=3|^|d=4|^|e=5|^|f=6|^|g=7|^|h=8|^|i=9|^|j=10|^|k=11|^|l=12|^|m=13|^|n=14|^|o=15|^|p=16|^|q=17|^|r=18|^|s=19|^|t=20|^|u=21|^|v=22|^|w=23|^|x=24|^|y=25|^|z=26|^|aa=27|^|ab=28|^|ac=29|^|ad=30|^|ae=31|^|af=32|^|ag=33|^|ah=34|^|ai=35|^|aj=36|^|ak=37|^|al=38|^|am=39|^|an=40|^|ao=41|^|ap=42|^|aq=43|^|ar=44|^|as=45|^|at=46|^|au=47|^|av=48|^|aw=49|^|ax=50";
sub double_split {
my %hash;
my @fields = split( /\Q|^|\E/, $line );
foreach my $field (@fields) {
my ( $name, $value ) = split( /=/, $field );
$hash{$name} = $value;
}
}
sub single_split {
my %hash = split( /\Q|^|\E|=/, $line );
}
sub re_replace_then_split {
$line =~ s/\Q|^|\E/=/g;
my %hash = split( /=/, $line );
}
sub single_regex {
my %hash = $line =~ m/(\w+)=(\w+)/g;
}
sub compound {
my %hash = $line =~ s/\Q|^|\E/\n/rg =~ m/(.*)=(.*)/g;
}
cmpthese(
1_000_000,
{ "Double Split" => \&double_split,
"single split with regex" => \&single_split,
"Replace then split" => \&re_replace_then_split,
"Single Regex" => \&single_regex,
"regex to linefeed them match" => \&compound
}
);
Run Code Online (Sandbox Code Playgroud)
看起来结果如下:
Rate Double Split single split with regex Single Regex Replace then split regex to linefeed them match
Double Split 18325/s -- -4% -34% -56% -97%
single split with regex 19050/s 4% -- -31% -54% -97%
Single Regex 27607/s 51% 45% -- -34% -96%
Replace then split 41733/s 128% 119% 51% -- -93%
regex to linefeed them match 641026/s 3398% 3265% 2222% 1436% --
Run Code Online (Sandbox Code Playgroud)
......我对最后一点感到有些怀疑,因为那太快了.可能会缓存那里发生的结果.
但看着它,正在减慢你的速度是正则表达式的变化:
sub single_split_with_alt {
my %hash = split( /\Q|^|\E|=/, $line );
}
sub single_split {
my %hash = split( /[\|\^\=]+/, $line );
}
Run Code Online (Sandbox Code Playgroud)
(我知道后者可能不是你想要的,但这只是为了说明目的)
得到:
Rate alternation single split
alternation 19135/s -- -37%
single split 30239/s 58% --
Run Code Online (Sandbox Code Playgroud)
但确实存在这样的问题,因为你的限制因素是磁盘IO,而不是CPU.
| 归档时间: |
|
| 查看次数: |
123 次 |
| 最近记录: |