好吧,这并不是我真正需要解决的问题,但我希望利用stackoverflow的集体智慧来看看是否有更快的方法来完成已经成功完成的工作。 :)
这里是东西。我有一个文本文件,实际上可以用作数据库(各种数据库)。文件中的每一行都包含由“ | ^ |”定界的名称/值对:
field1=value1|^|field2=value2|^|field3=value3|^|field4=value4
有成千上万的线。通过“数据库”搜索是顺序的:
open( DB, "<file.dat" ) || die( "Can't open file.dat\n" );
# Searching for "John" in field "Name"
my $searchName = "John"
foreach my $rec ( <DB> ) {
my $value = ($rec =~ /(?:^|\Q|^|\E)Name=(.*?)(?:\Q|^|\E|$)/)[0];
if( $value =~ /^$searchName$/i ) {
# Found it!
last;
}
}
实际上这是最快的搜索方式吗?还是还有其他我不知道的技巧?
编辑:
只是想添加以下观察,这使我非常惊讶。当我这样做时:
my $searchName = "John";
foreach my $rec ( <DB> ) {
if( $rec =~ /(?:^|\Q|^|\E)Name=$searchName(?:\Q|^|\E|$)/ ) {
# Found it!
last;
}
}
这实际上是相当快的(在45,000个记录数据库上大约需要0.1秒。
但是当我这样做时:
my $searchName = "John";
foreach my $rec ( <DB> ) {
if( $rec =~ /(?:^|\Q|^|\E)Name=$searchName(?:\Q|^|\E|$)/i ) {
# Found it!
last;
}
}
唯一的区别是RegEx中不区分大小写的“ i”,现在一下子要花20秒!慢了200倍!
不需要每行执行两个正则表达式匹配。
if ($rec =~ /(?:^|\Q|^|\E)Name=(?i:\Q$searchName\E)(?:\Q|^|\E|$)/)
这也避免捕获。
您可能会从循环外编译常量正则表达式中受益。
my $re = qr/(?:^|\Q|^|\E)Name=(?i:\Q$searchName\E)(?:\Q|^|\E|$)/;
if ($rec =~ $re)
您可能会受益于将整个文件加载到内存中。
您可以将第二个文件中的数据标准化,因此搜索速度更快。如果您足够勇敢,则可以使用三字母组合词,仅保存前三个字母并创建反向索引。您可以使用行中的所有数据来执行此操作,例如:
abc joh mar ant
找到与倒排索引匹配的行后,您将测试结果。对于大量数据,这可能会非常快。我做了类似的事情来在数以百万计的文本中发现窃,而且它的速度很快。
也许下面的代码适合您的'账单'
use strict;
use warnings;
use feature 'say';
my $filename = 'db.dat';
my $name = 'John';
my $search = qr/Name=($name)/;
my $record;
open DB, "< $filename"
or die "Couldn't open $filename";
map { $record = $_ if /$search/ } <DB>;
say "Record: $record" if defined $record;
say "Found: $1" if $record =~ /$search/;
数据样本db.dat
some=field|^|Name=Alex|^|Last=Starsky
some=other|^|Name=Stas|^|Last=Venichevsky
some=target|^|Name=John|^|Last=Smith
some=waste|^|Name=Bill|^|Last=Gates
some=wheels|^|Name=William|^|Last=Ford
输出
Record: some=target|^|Name=John|^|Last=Smith
Found: John
带有子例程的替代版本
use strict;
use warnings;
use feature 'say';
my $filename = 'db.dat';
my $name = 'John';
my $search = qr/Name=($name)/;
my $record;
open DB, "< $filename"
or die "Couldn't open $filename";
map { found($_) if /$search/ } <DB>;
sub found {
my $record = shift;
chomp $record;
say "Record: $record";
say "Found: $1\n" if $record =~ /$search/;
}
数据样本db.dat
some=field|^|Name=Alex|^|Last=Starsky
some=other|^|Name=Stas|^|Last=Venichevsky
some=target|^|Name=John|^|Last=Smith
some=waste|^|Name=Bill|^|Last=Gates
some=wheels|^|Name=William|^|Last=Ford
some=star|^|Name=John|^|Last=Incognito
some=magic|^|Name=David|^|Last=Copperfield
输出
Record: some=target|^|Name=John|^|Last=Smith
Found: John
Record: some=star|^|Name=John|^|Last=Incognito
Found: John
将文件加载到内存中,使用正确的正则表达式是朝正确方向的步骤,使用并行处理(-> https://metacpan.org/pod/distribution/MCE/lib/MCE.pod)是另一步骤。您可能需要看一下这个perl5实现:https://github.com/leendo/hello-world。
似乎可以做您想要的一切。