Regex perl正则表达式大数据性能
1.)我从数据库中读取了大量数据(约1000万条记录)。 2.)对于每个记录,我搜索并替换我拥有的大约500个正则表达式。 3.)应用所有500个正则表达式后,记录将写入文件,然后处理下一条记录 性能瓶颈是对从数据库获取的每条记录应用500个正则表达式 以下是相关代码块:Regex perl正则表达式大数据性能,regex,performance,algorithm,perl,Regex,Performance,Algorithm,Perl,1.)我从数据库中读取了大量数据(约1000万条记录)。 2.)对于每个记录,我搜索并替换我拥有的大约500个正则表达式。 3.)应用所有500个正则表达式后,记录将写入文件,然后处理下一条记录 性能瓶颈是对从数据库获取的每条记录应用500个正则表达式 以下是相关代码块: #normalizing the addresses fetched... this may take awhile while(my @row = $queryHandle->fetchrow_array())
#normalizing the addresses fetched... this may take awhile
while(my @row = $queryHandle->fetchrow_array())
{
#extract data from record
$accountKey = @row[0];
$addressLine1 = @row[1];
$addressLine2 = @row[2];
#iterate through all the regular expressions I have stored (about 500)
for my $regexRef (@regexesList)
{
#get regular expression hash object
my %regexObj = %{$regexRef};
my $regexPattern = $regexObj{pattern}; #the regex pattern
my $regexOutput = $regexObj{output}; #the replacement string
#first remove all special characters leaving only numbers and alphabets
$addressLine1 =~ s/[^A-Za-z0-9 ]//g;
$addressLine2 =~ s/[^A-Za-z0-9 ]//g;
#now standardize the addresses
$addressLine1 =~ s/$regexPattern/$regexOutput/ig;
$addressLine2 =~ s/$regexPattern/$regexOutput/ig;
}
my $normalizedAddress = lc($addressLine1 . $addressLine2);
$normalizedAddress =~ s/\s+//g; #remove all white space
print $dataFileHandle "${normalizedAddress}\n";
$rowCount++;
}
这是工作代码,但性能非常差。目前该脚本已经运行了2.5个小时,已经向输出文件写入了313万条记录,还有大约700万条记录,哈哈
这是最好的吗?还有其他更快或更慢的方法吗?也许先将每一行写入一个文件,然后在整个文件上运行每个正则表达式
在我尝试上述替代方案之前,我想知道是否有更好的方法来实现这一点。首先,您正在进行大量不必要的ref和deref。因此,在@regexesList中有一个regex列表,其中显然充满了hashrefs。在迭代时,对每个hashref进行解引用,这会在内存中复制它,然后从复制的哈希中取出项目 所以,第一条建议是,不要这样做。这将代码中的for循环简化为:
for my $regexRef (@regexesList)
{
#first remove all special characters leaving only numbers and alphabets
$addressLine1 =~ s/[^A-Za-z0-9 ]//g;
$addressLine2 =~ s/[^A-Za-z0-9 ]//g;
#now standardize the addresses
$addressLine1 =~ s/$regexRef->{pattern}/$regexRef->{output}/ig;
$addressLine2 =~ s/$regexRef->{pattern}/$regexRef->{output}/ig;
}
接下来,尝试将更多工作卸载到数据库中。当我使用mysql进行这类工作时,我会将尽可能多的简单任务卸载到数据库中,因为这些类型的字符串函数在那里要快得多。我使用了大量的LOWER()、CONCAT()、CONCAT_WS()和REPLACE(),它们在这里也可以为您服务。首先,您正在进行大量不必要的ref和deref。因此,在@regexesList中有一个regex列表,其中显然充满了hashrefs。在迭代时,对每个hashref进行解引用,这会在内存中复制它,然后从复制的哈希中取出项目 所以,第一条建议是,不要这样做。这将代码中的for循环简化为:
for my $regexRef (@regexesList)
{
#first remove all special characters leaving only numbers and alphabets
$addressLine1 =~ s/[^A-Za-z0-9 ]//g;
$addressLine2 =~ s/[^A-Za-z0-9 ]//g;
#now standardize the addresses
$addressLine1 =~ s/$regexRef->{pattern}/$regexRef->{output}/ig;
$addressLine2 =~ s/$regexRef->{pattern}/$regexRef->{output}/ig;
}
接下来,尝试将更多工作卸载到数据库中。当我使用mysql进行这类工作时,我会将尽可能多的简单任务卸载到数据库中,因为这些类型的字符串函数在那里要快得多。我使用了大量的LOWER()、CONCAT()、CONCAT_WS()和REPLACE(),它们在这里也可以为您服务。您每次都要重新分析500-600个正则表达式,这需要时间
$addressLine1 =~ s/$regexPattern/$regexOutput/ig; # Interpolate and reparse
下面是一个概念证明,它构建了一个匿名子例程,其中包含文本代码中的正则表达式,而不是每次都从变量进行解释
这表明性能提高了10倍
use strict;
use warnings;
use Benchmark;
my @regexesList = map {{pattern => "foo$_", output => "bar$_"}} (1..600);
my $string1 = 'a' x 100;
my $string2 = 'b' x 100;
# Original code
sub original {
my ($regexesList, $addressLine1, $addressLine2) = @_;
#iterate through all the regular expressions I have stored (about 500)
for my $regexRef (@regexesList) {
#get regular expression hash object
my %regexObj = %{$regexRef};
my $regexPattern = $regexObj{pattern}; #the regex pattern
my $regexOutput = $regexObj{output}; #the replacement string
#now standardize the addresses
$addressLine1 =~ s/$regexPattern/$regexOutput/ig;
$addressLine2 =~ s/$regexPattern/$regexOutput/ig;
}
my $normalizedAddress = lc($addressLine1 . $addressLine2);
$normalizedAddress =~ s{\s+}{}g; #remove all white space
return $normalizedAddress;
}
# Build an anonymous subroutine to do all of the regex translations:
my $regex_code = "s/\\s+//g;\n";
for (@regexesList) {
$regex_code .= "s/$_->{pattern}/$_->{output}/ig;\n";
}
my $code = <<"END_CODE";
sub {
my \@address = \@_;
for (\@address) {
$regex_code
}
return lc join '', \@address;
}
END_CODE
my $address_sub = eval $code;
if ($@) {
die "Invalid code $code: $@";
}
# Benchmark these two calling methods:
timethese(10000, {
'original' => sub { original(\@regexesList, $string1, $string2) },
'cached' => sub { $address_sub->($string1, $string2) },
});
此外,您不必要地应用了这个正则表达式s/[^A-Za-z0-9]//g代码>用于循环的每个迭代。这是不必要的,可以在循环之外应用
可能还有其他可以改进的地方,但您必须利用自己的力量来找到它们,因为这并不是这样做的真正目的。您每次都要重新分析500-600个正则表达式,这需要时间
$addressLine1 =~ s/$regexPattern/$regexOutput/ig; # Interpolate and reparse
下面是一个概念证明,它构建了一个匿名子例程,其中包含文本代码中的正则表达式,而不是每次都从变量进行解释
这表明性能提高了10倍
use strict;
use warnings;
use Benchmark;
my @regexesList = map {{pattern => "foo$_", output => "bar$_"}} (1..600);
my $string1 = 'a' x 100;
my $string2 = 'b' x 100;
# Original code
sub original {
my ($regexesList, $addressLine1, $addressLine2) = @_;
#iterate through all the regular expressions I have stored (about 500)
for my $regexRef (@regexesList) {
#get regular expression hash object
my %regexObj = %{$regexRef};
my $regexPattern = $regexObj{pattern}; #the regex pattern
my $regexOutput = $regexObj{output}; #the replacement string
#now standardize the addresses
$addressLine1 =~ s/$regexPattern/$regexOutput/ig;
$addressLine2 =~ s/$regexPattern/$regexOutput/ig;
}
my $normalizedAddress = lc($addressLine1 . $addressLine2);
$normalizedAddress =~ s{\s+}{}g; #remove all white space
return $normalizedAddress;
}
# Build an anonymous subroutine to do all of the regex translations:
my $regex_code = "s/\\s+//g;\n";
for (@regexesList) {
$regex_code .= "s/$_->{pattern}/$_->{output}/ig;\n";
}
my $code = <<"END_CODE";
sub {
my \@address = \@_;
for (\@address) {
$regex_code
}
return lc join '', \@address;
}
END_CODE
my $address_sub = eval $code;
if ($@) {
die "Invalid code $code: $@";
}
# Benchmark these two calling methods:
timethese(10000, {
'original' => sub { original(\@regexesList, $string1, $string2) },
'cached' => sub { $address_sub->($string1, $string2) },
});
此外,您不必要地应用了这个正则表达式s/[^A-Za-z0-9]//g代码>用于循环的每个迭代。这是不必要的,可以在循环之外应用
可能还有其他可以改进的地方,但您必须利用自己的力量来找到它们,因为这并不是这样做的真正目的。转到codereview,准确解释您正在尝试做什么以及为什么需要应用500 regex。这是为了标准化用户地址,如St.,str.,street,road,rd.,drive,博士等等。USPS邮政地址标准的完整列表接近600。USPS对此有足够的支持。@friedo该API的地址验证需要批准,我的时间很短=(次要建议:tr/A-Za-z0-9//dc
将比s/[^A-Za-z0-9]更快)//g
。转到codereview,准确解释您正在尝试做什么以及为什么需要应用500正则表达式。这是为了标准化用户地址,如St.,str.,street,road,rd.,drive,dr.等。USPS邮政地址标准的完整列表接近600。USPS对此有详细说明。@friedo该API要求的地址验证ires批准和我的时间不足=(次要建议:tr/A-Za-z0-9//dc
将比s/[^A-Za-z0-9]更快)//g
。谢谢你的提示!我是一个Perl新手,匿名子例程中有一行让我难堪的代码,$regex\u代码是如何应用于for循环中的\@地址的?请原谅noob问题你可以打印出$code
的值来查看我完整构建的匿名子例程。它使用循环(@地址)
将每个地址行分配给全局变量$\
。然后每个正则表达式替换s/../../ig
在默认情况下在$\
上工作。啊,我不知道正则表达式在默认情况下在$上工作,即使$未写入代码块。非常感谢!看起来我在perlthanks f上还有更多的阅读要做或者提示!我是Perl的新手,在匿名子例程中有一行让我感到困惑,$regex_代码是如何应用于for循环中的\@address的?请原谅noob问题您可以打印出$code
的值来查看我完整构建的匿名子例程。它使用循环for(@address)
将每个地址行分配给全局变量$\u
。然后每个正则表达式替换s/../…/ig
在默认情况下在$\u
上工作。啊,我不知道正则表达式在de下正在处理$