虽然这是非常基本的,但我找不到类似的问题,所以如果你知道现有的问题/解决方案,请链接到一个。
我有一个大约2MB且大约16,000行的.txt
文件。每个记录长度为160个字符,阻塞因子为10.这是一种较旧的数据结构类型,几乎看起来像制表符分隔文件,但分隔是单字符/空格。
首先,我glob
.txt
个文件的目录 - 目录中一次只有一个文件,因此这种尝试本身可能效率低下。
my $txt_file = glob "/some/cheese/dir/*.txt";
然后我用这一行打开文件:
open (F, $txt_file) || die ("Could not open $txt_file");
根据此文件的数据字典,我在while循环中使用Perl的substr()
函数解析每行中的每个“字段”。
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);
}
<小时/> 这种方法需要花费大量时间来处理每一行,我想知道是否有更有效的方法从文件的每一行中获取每个字段。
任何人都可以提出更有效/首选的方法吗?
答案 0 :(得分:8)
在我看来,您正在使用固定宽度字段。真的吗?如果是,unpack
功能就是您所需要的。您提供字段的模板,它将从这些字段中提取信息。有tutorial可用,模板信息可在pack
的文档中找到,它是unpack
的逻辑逆。简单来说就是一个基本的例子:
my @values = unpack("A1 A15 A15 ...", $line);
其中'A'表示任何文本字符(据我所知),数字是多少。有些人使用它,unpack
有很多艺术,但我相信这足以满足基本用途。
答案 1 :(得分: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% --
输入是一个包含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)
答案 2 :(得分:0)
在每一行上进行拆分,如下所示:
my @values = split(/\s/,$line);
然后使用您的价值观。
答案 3 :(得分:0)
您可以执行以下操作:
while ($line = <F>){
if ($line =~ /(.{1}) (.{15}) ........ /){
$nom_stat = $1;
$lname = $2;
...
}
}
我认为它比你的substr建议更快,但我不确定它是否是最快的解决方案,但我认为它可能很好。