Perl:寻找更快的搜索(通过文件)

问题描述 投票:2回答:5

好吧,这并不是我真正需要解决的问题,但我希望利用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倍!

perl search
5个回答
3
投票

不需要每行执行两个正则表达式匹配。

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)

您可能会受益于将整个文件加载到内存中。


1
投票

您可以将第二个文件中的数据标准化,因此搜索速度更快。如果您足够勇敢,则可以使用三字母组合词,仅保存前三个字母并创建反向索引。您可以使用行中的所有数据来执行此操作,例如:

abc joh mar ant

找到与倒排索引匹配的行后,您将测试结果。对于大量数据,这可能会非常快。我做了类似的事情来在数以百万计的文本中发现窃,而且它的速度很快。


0
投票

也许下面的代码适合您的'账单'

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

0
投票

带有子例程的替代版本

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

0
投票

将文件加载到内存中,使用正确的正则表达式是朝正确方向的步骤,使用并行处理(-> https://metacpan.org/pod/distribution/MCE/lib/MCE.pod)是另一步骤。您可能需要看一下这个perl5实现:https://github.com/leendo/hello-world

似乎可以做您想要的一切。

© www.soinside.com 2019 - 2024. All rights reserved.