Arrays 如何使用Perl更高效地处理大型文本文件

Arrays 如何使用Perl更高效地处理大型文本文件,arrays,perl,Arrays,Perl,问题: 我需要搜索一个巨大的文本文件(包含大约150万行数据),提取与唯一ID匹配的行。我将唯一ID存储在一个数组中,并在每个数组元素中遍历整个文件一次 虽然这种方法适用于小型阵列,但如果阵列相当大,它会大大降低我的程序的速度,因为要执行的操作太多了 我的数组最多可以包含10000个唯一标识符,其形式如下: DC888U1 DC888U2 DC888U3 ... ... 数据文件中的行始终以唯一标识符开头 DC888U1苹果0.99 75 DC888U2橙子0.75 1002 DC888U3

问题:

我需要搜索一个巨大的文本文件(包含大约150万行数据),提取与唯一ID匹配的行。我将唯一ID存储在一个数组中,并在每个数组元素中遍历整个文件一次

虽然这种方法适用于小型阵列,但如果阵列相当大,它会大大降低我的程序的速度,因为要执行的操作太多了

我的数组最多可以包含10000个唯一标识符,其形式如下:

DC888U1
DC888U2
DC888U3
... 
...
数据文件中的行始终以唯一标识符开头

DC888U1苹果0.99 75
DC888U2橙子0.75 1002
DC888U3面包1.35 100
... ... ... ...
... ... ... ...
我的代码如下:

#array containing identifiers
open (IDENTIFIERS "< keywords.txt") or die "Cannot open file: $!";
    chomp(my @keywords = <IDENTIFIERS>);
close (IDENTIFIERS);

#iterate through the array element by element
foreach my $element (@keywords) {
    open (FH "< inventory.txt") or die "cannot open file: $!";
    while (<FH>) {
        if ($_ =~ /^\Q$element\E/) {
            print $_;
        }
    }
close (FH);
}
包含标识符的数组 打开(标识符“ 我已经查看了Tie::File,看看它是否可以加快我的处理速度,但没有运气。我想知道是否有一种方法可以缓存已经打印的行,这样当我下次通过文件时,每次要搜索的数据量都会减少


有吗

关键是将O(N*M)代码转换为O(N+M):

使用严格;
使用警告;
使用v5.10;#汽车模具
使用自动模具;

die您要求进行优化,这在很大程度上取决于上下文

如果您对文件进行了排序(“排序”实际上意味着“根据您自己的标准进行排序”),您可以决定浪费一些磁盘空间,并创建一个新文件,其中包含填充为具有相同长度的相同行

然后对该文件使用二进制搜索,以获取所查找标识符的至少一个匹配项的行号(这就是为什么需要相同的行长,否则对该文件的搜索将无法正常工作)

如果标识符在文件中是唯一的,那么就完成了。 如果不是,则只需上移一行,直到标识符发生变化,然后下移一行,直到标识符发生变化,并获得间隔


再次强调:这只在文件被排序并且行的长度都相同的情况下才有效,但是如果是这样的话,那么您将看到速度的巨大提高。我知道,因为我自己这样做是为了在一个200Mb+的文本文件中搜索:)

对于最常见的行长度,150万行数据并不是一个巨大的数据量。如果每条线都是1K,那么就有1.5GB的数据,即使在我十年前的笔记本电脑上,这些数据也可以整齐地存储在内存中

您的问题是由于您对每个标识符反复处理文件

因此,如果您有10000个标识符,并且处理该文件需要1秒,那么您的处理仍然需要3小时。如果处理该文件需要一分钟,则您的方法将需要7天

将10000个标识符作为密钥放入散列。然后,在遍历文件时,捕获每行上非空格字符的初始序列,检查它是否是散列中的键;如果是,请打印

未经测试:

 my %keywords = map { $_ => undef } @keywords;
 while (my $line = <$in>) {
     my ($id) = ($line =~ /^(\S+)/);
     if (exists $keywords{$id}) {
          print $line;
     }
 }
my%keywords=map{$\=>unde}@keywords;
while(我的$line=){
我的($id)=($line=~/^(\S+/);
if(存在$keywords{$id}){
打印$行;
}
}

除非您处理正则表达式没有的情况,否则
^
锚定是没有意义的match@Borodin我写下我的意思。如果标识符应该在行的开头,我就这样写。答案的要点是避免重复读取文件。。。OP可以处理其余部分,这取决于数据行上是否允许前导空格。@Borodin:
perl-E'my$re=“aa | bb”$re=qr/$re/;说“不,不,博罗丁。”除非“abb”=~/^$re/'
my %keywords;
{
    my $keywords_file = shift;
    open my $fh, '<', $keywords_file;
    @keywords{ map s/\s//gr, <$fh> } = (); #/ make syntax highlight happy
};

while (<>) { print if /^(\S+)/ and exists $keywords{$1} }
 my %keywords = map { $_ => undef } @keywords;
 while (my $line = <$in>) {
     my ($id) = ($line =~ /^(\S+)/);
     if (exists $keywords{$id}) {
          print $line;
     }
 }