【发布时间】:2011-12-19 21:26:38
【问题描述】:
我主要是 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
我的脚本的基本逻辑是:
- 阅读直到 ---- 行以构建要提取的参数列表(始终具有“过滤器 =”)。
- 使用 --- 行来确定字段宽度。它被空格分隔。
- 对于每个参数的构建时间和数据数组(嵌套在 foreach 参数中)
- 在
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);
所以我想问你们这些网络好人如下:
- 我显然在搞砸什么?语法、在我不需要时声明变量等。
- 这可能很慢(猜测),因为嵌套循环和一遍又一遍地逐行搜索。有没有更好的方法来重组循环以一次提取多行?
- 您还有其他提高速度的建议吗?
编辑:我修改了示例文本文件以说明非整数时间戳,并且参数名称可能包含空格。
【问题讨论】:
-
您能否在上面的示例中显示您对 TOC 文件和 BIN 文件的期望?
-
@SinanÜnür TOC 文件看起来像这样:注意偏移量是由组成的。 Param1,UI,0,10,20, Param2,D,20,30,40, Param3,TXT,40,50,60, 其中格式为 Name, type, offset to timeStart, offset to time end, offset to数据结束。因此,在 Matlab 中需要做的就是使用适当的数据类型从开始到结束偏移量读取二进制文件。
-
@SinanÜnür 我只会写出第一个参数,因为它会以二进制形式出现。我将使用十六进制表示法,尽管这将是一个二进制文件。此外,我将时间戳写为单打而不是双打作为空格。 0x3f800000 0x40400000 0x40a00000 0x00000005 0x0000000A 0x0000000F。所以 Param1 的 timestart、timeend 和 datastart 偏移量为 0,96,192(如果我正确添加的话)
-
一定要按参数名排序吗?
-
@BradGilbert 如果在写入二进制时未按参数名称排序,则在 Matlab 中构建数据/时间数组很可能会很困难且速度很慢。
标签: perl