我主要是一个 Matlab 用户和 Perl n00b。这是我的第一个 Perl 脚本。
我有一个大型固定宽度数据文件,我想将其处理成带有目录的二进制文件。我的问题是数据文件非常大,并且数据参数按时间排序。这使得解析到 Matlab 变得困难(至少对我来说)。因此,看到 Matlab 不太擅长解析文本,我想我应该尝试 Perl。我编写了以下代码,该代码可以工作......至少在我的小测试文件上。然而,当我在实际的大数据文件上尝试时,速度非常慢。它是从 Web/Perl 文档中拼凑而成的各种任务的大量示例。
这是数据文件的一个小样本。注:真实文件大约有2000个参数,大小为1-2GB。参数可以是文本、双精度数或无符号整数。
Param 1 filter = ALL_VALUES
Param 2 filter = ALL_VALUES
Param 3 filter = ALL_VALUES
Time Name Ty Value
---------- ---------------------- --- ------------
1.1 Param 1 UI 5
2.23 Param 3 TXT Some Text 1
3.2 Param 1 UI 10
4.5 Param 2 D 2.1234
5.3 Param 1 UI 15
6.121 Param 2 D 3.1234
7.56 Param 3 TXT Some Text 2
我的脚本的基本逻辑是:
- 阅读直到 ---- 行来构建要提取的参数列表(始终具有“filter =”)。
- 使用 --- 行确定字段宽度。它被空格打破了。
- 对于每个参数构建时间和数据数组(嵌套在 foreach 参数内)
- In
continue
块将时间和数据写入二进制文件。然后在文本目录文件中记录名称、类型和偏移量(用于稍后将文件读入 Matlab)。
这是我的脚本:
#!/usr/bin/perl
$lineArg1 = @ARGV[0];
open(INFILE, $lineArg1);
open BINOUT, '>:raw', $lineArg1.".bin";
open TOCOUT, '>', $lineArg1.".toc";
my $line;
my $data_start_pos;
my @param_name;
my @template;
while ($line = <INFILE>) {
chomp $line;
if ($line =~ s/\s+filter = ALL_VALUES//) {
$line = =~ s/^\s+//;
$line =~ s/\s+$//;
push @param_name, $line;
}
elsif ($line =~ /^------/) {
@template = map {'A'.length} $line =~ /(\S+\s*)/g;
$template[-1] = 'A*';
$data_start_pos = tell INFILE;
last; #Reached start of data exit loop
}
}
my $template = "@template";
my @lineData;
my @param_data;
my @param_time;
my $data_type;
foreach $current_param (@param_name) {
@param_time = ();
@param_data = ();
seek(INFILE,$data_start_pos,0); #Jump to data start
while ($line = <INFILE>) {
if($line =~ /$current_param/) {
chomp($line);
@lineData = unpack $template, $line;
push @param_time, @lineData[0];
push @param_data, @lineData[3];
}
} # END WHILE <INFILE>
} #END FOR EACH NAME
continue {
$data_type = @lineData[2];
print TOCOUT $current_param.",".$data_type.",".tell(BINOUT).","; #Write name,type,offset to start time
print BINOUT pack('d*', @param_time); #Write TimeStamps
print TOCOUT tell(BINOUT).","; #offset to end of time/data start
if ($data_type eq "TXT") {
print BINOUT pack 'A*', join("\n",@param_data);
}
elsif ($data_type eq "D") {
print BINOUT pack('d*', @param_data);
}
elsif ($data_type eq "UI") {
print BINOUT pack('L*', @param_data);
}
print TOCOUT tell(BINOUT).","."\n"; #Write memory loc to end data
}
close(INFILE);
close(BINOUT);
close(TOCOUT);
所以我向各位网络上的好心人提出以下问题:
- 我明显搞砸了什么?语法、不需要时声明变量等。
- 由于嵌套循环和一遍又一遍地逐行搜索,这可能很慢(猜测)。有没有更好的方法来重构循环以一次提取多行?
- 您还可以提供其他任何提高速度的技巧吗?
编辑:我修改了示例文本文件以说明非整数时间戳和参数名称可能包含空格。