我主要是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);
所以我的問題給你良好的網絡人如下:
- 我是什麼明顯搞砸了?語法,當我不需要時聲明變量等。
- 由於嵌套循環和一遍又一遍搜索行,這可能很慢(猜測)。有沒有更好的方法來重構循環以一次提取多行?
- 任何其他速度改進提示,你可以給?
編輯:我修改了示例文本文件來說明非整數時間戳,並且參數名可能包含空格。
Perl在您的命令行上有文檔可用:'perldoc -q profile' – toolic 2011-12-19 21:47:23
您可以在TOC文件和上述示例的BIN文件中顯示您期望的內容嗎? – 2011-12-19 23:23:52
@SinanÜnürTOC文件看起來像這樣: 請注意,補償號碼是由組成的。 Param1,UI,0,10,20, Param2,D,20,30,40, Param3,TXT,40,50,60, 其中格式爲Name,type,offset to timeStart,offset to時間結束,抵消數據結束。所以在Matlab中需要的是使用適當的數據類型將二進制文件從開始到結束偏移量進行fread。 – 2011-12-20 00:33:37