使用Perl解析文本文件的最有效方法是什么?

Che*_*eso 6 optimization perl parsing field text-files

虽然这是非常基本的,但我找不到类似的问题,所以如果你知道现有的问题/解决方案,请链接到一个.


我有一个.txt大约2MB和大约16,000行的文件.每个记录长度为160个字符,阻塞因子为10.这是一种较旧的数据结构类型,几乎看起来像制表符分隔文件,但是分隔是单字符/空格.

首先,我glob对目录的.txt文件-从来就没有在一个时间该目录多个文件,所以这种尝试本身可以是低效的.

my $txt_file = glob "/some/cheese/dir/*.txt";
Run Code Online (Sandbox Code Playgroud)

然后我用这一行打开文件:

open (F, $txt_file) || die ("Could not open $txt_file");
Run Code Online (Sandbox Code Playgroud)

根据这个文件的数据字典,我substr()在while循环中使用Perl的函数解析每一行中的每个"字段" .

while ($line = <F>)
{
$nom_stat   = substr($line,0,1);
$lname      = substr($line,1,15);
$fname      = substr($line,16,15);
$mname      = substr($line,31,1);
$address    = substr($line,32,30);
$city       = substr($line,62,20);
$st         = substr($line,82,2);
$zip        = substr($line,84,5);
$lnum       = substr($line,93,9);
$cl_rank    = substr($line,108,4);
$ceeb       = substr($line,112,6);
$county     = substr($line,118,2);
$sex        = substr($line,120,1);
$grant_type = substr($line,121,1);
$int_major  = substr($line,122,3);
$acad_idx   = substr($line,125,3);
$gpa        = substr($line,128,5);
$hs_cl_size = substr($line,135,4);
}
Run Code Online (Sandbox Code Playgroud)


这种方法需要花费大量时间来处理每一行,我想知道是否有更有效的方法从文件的每一行中获取每个字段.

有谁能建议更有效/首选的方法?

Joe*_*ger 8

它在我看来你在这里使用固定宽度字段.真的吗?如果是,unpack功能就是您所需要的.您提供字段的模板,它将从这些字段中提取信息.有一个教程可用,模板信息的文档中找到packunpack的逻辑相反.仅作为一个基本的例子:

my @values = unpack("A1 A15 A15 ...", $line);
Run Code Online (Sandbox Code Playgroud)

其中'A'表示任何文字字符(据我所知),数字是多少.unpack有些人使用它有很多艺术,但我相信这足以满足基本用途.


Ian*_* C. 4

使用该选项编译和缓存的单个正则表达式/o是最快的方法。我使用 Benchmark 模块以三种方式运行你的代码并得出:

         Rate unpack substr regexp
 unpack 2.59/s     --   -59%   -67%
 substr 6.23/s   141%     --   -21%
 regexp 7.90/s   206%    27%     --
Run Code Online (Sandbox Code Playgroud)

输入是一个包含 20k 行的文件,每行有相同的 160 个字符(字符重复 16 次0123456789)。因此它的输入大小与您正在使用的数据相同。

该方法从最慢最快Benchmark::cmpthese()输出子例程调用。第一列告诉我们子例程每秒可以运行多少次。正则表达式方法是最快的。不像我之前所说的那样打开包装。对于那个很抱歉。

基准代码如下。打印语句作为健全性检查。这是为 darwin-thread-multi-2level 构建的 Perl 5.10.0。

#!/usr/bin/env perl
use Benchmark qw(:all);
use strict;

sub use_substr() {
    print "use_substr(): New itteration\n";
    open(F, "<data.txt") or die $!;
    while (my $line = <F>) {
        my($nom_stat, 
           $lname,   
           $fname,      
           $mname,    
           $address,     
           $city,    
           $st,       
           $zip,         
           $lnum,        
           $cl_rank,
           $ceeb,    
           $county,
           $sex,     
           $grant_type,
           $int_major, 
           $acad_idx,  
           $gpa,   
           $hs_cl_size) = (substr($line,0,1),
                           substr($line,1,15),
                           substr($line,16,15),
                           substr($line,31,1),
                           substr($line,32,30),
                           substr($line,62,20),
                           substr($line,82,2),
                           substr($line,84,5),
                           substr($line,93,9),
                           substr($line,108,4),
                           substr($line,112,6),
                           substr($line,118,2),
                           substr($line,120,1),
                           substr($line,121,1),
                           substr($line,122,3),
                           substr($line,125,3),
                           substr($line,128,5),
                           substr($line,135,4));
       #print "use_substr(): \$lname = $lname\n";
       #print "use_substr(): \$gpa   = $gpa\n";
    }    
    close(F);
    return 1;
}

sub use_regexp() {
    print "use_regexp(): New itteration\n";
    my $pattern = '^(.{1})(.{15})(.{15})(.{1})(.{30})(.{20})(.{2})(.{5})(.{9})(.{4})(.{6})(.{2})(.{1})(.{1})(.{3})(.{3})(.{5})(.{4})';
    open(F, "<data.txt") or die $!;
    while (my $line = <F>) {
        if ( $line =~ m/$pattern/o ) {
            my($nom_stat, 
               $lname,   
               $fname,      
               $mname,    
               $address,     
               $city,    
               $st,       
               $zip,         
               $lnum,        
               $cl_rank,
               $ceeb,    
               $county,
               $sex,     
               $grant_type,
               $int_major, 
               $acad_idx,  
               $gpa,   
               $hs_cl_size) = ( $1,
                                $2,
                                $3,
                                $4,
                                $5,
                                $6,
                                $7,
                                $8,
                                $9,
                                $10,
                                $11,
                                $12,
                                $13,
                                $14,
                                $15,
                                $16,
                                $17,
                                $18);
            #print "use_regexp(): \$lname = $lname\n";
            #print "use_regexp(): \$gpa   = $gpa\n";
        }
    }    
    close(F);
    return 1;
}

sub use_unpack() {
    print "use_unpack(): New itteration\n";
    open(F, "<data.txt") or die $!;
    while (my $line = <F>) {
        my($nom_stat, 
           $lname,   
           $fname,      
           $mname,    
           $address,     
           $city,    
           $st,       
           $zip,         
           $lnum,        
           $cl_rank,
           $ceeb,    
           $county,
           $sex,     
           $grant_type,
           $int_major, 
           $acad_idx,  
           $gpa,   
           $hs_cl_size) = unpack(
               "(A1)(A15)(A15)(A1)(A30)(A20)(A2)(A5)(A9)(A4)(A6)(A2)(A1)(A1)(A3)(A3)(A5)(A4)(A*)", $line
               );
        #print "use_unpack(): \$lname = $lname\n";
        #print "use_unpack(): \$gpa   = $gpa\n";
    }
    close(F);
    return 1;
}

# Benchmark it
my $itt = 50;
cmpthese($itt, {
        'substr' => sub { use_substr(); },
        'regexp' => sub { use_regexp(); },
        'unpack' => sub { use_unpack(); },
    }
);
exit(0)
Run Code Online (Sandbox Code Playgroud)