2011-02-06 77 views
8

我有很多的文本文件与固定宽度的字段:解析固定宽度的文件

<c>  <c>  <c> 
Dave Thomas 123 Main 
Dan  Anderson 456 Center 
Wilma Rainbow 789 Street 

文件的其余部分是在一个类似的格式,其中<c>将迎来一列的开始,但他们有各种(未知的)列宽度&。解析这些文件的最佳方法是什么?

我试着用Text::CSV,但因为没有分隔符,很难得到一致的结果(除非我正在使用的模块错误):

my $csv = Text::CSV->new(); 
$csv->sep_char (' '); 

while (<FILE>){ 
    if ($csv->parse($_)) { 
     my @columns=$csv->fields(); 
     print $columns[1] . "\n"; 
    } 
} 
+1

你为什么反对“解析”标签?这是一个解析问题。你在Perl中需要一个解决方案并不意味着它不是一个解析问题。 – zwol 2011-02-06 02:31:27

+0

,因为我不想要一个通用的解决方案 – 2011-02-06 02:33:00

+0

也许我误解了......我认为把“解析”放在那里会带来一大堆与我的情况无关的解决方案(即python,php等)。 ..thx – 2011-02-06 02:42:19

回答

12

正如user604939提到的,unpack是用于固定宽度字段的工具。但是,unpack需要传递模板才能使用。既然你说你的域可以改变宽度,解决的办法是从文件的第一行建立这个模板:

my @template = map {'A'.length}  # convert each to 'A##' 
       <DATA> =~ /(\S+\s*)/g; # split first line into segments 
$template[-1] = 'A*';     # set the last segment to be slurpy 

my $template = "@template"; 
print "template: $template\n"; 

my @data; 
while (<DATA>) { 
    push @data, [unpack $template, $_] 
} 

use Data::Dumper; 

print Dumper \@data; 

__DATA__ 
<c>  <c>  <c> 
Dave Thomas 123 Main 
Dan  Anderson 456 Center 
Wilma Rainbow 789 Street 

它打印:

 
template: A8 A10 A* 
$VAR1 = [ 
      [ 
      'Dave', 
      'Thomas', 
      '123 Main' 
      ], 
      [ 
      'Dan', 
      'Anderson', 
      '456 Center' 
      ], 
      [ 
      'Wilma', 
      'Rainbow', 
      '789 Street' 
      ] 
     ]; 
3

只需使用Perl的unpack功能。事情是这样的:

while (<FILE>) { 
    my ($first,$last,$street) = unpack("A9A25A50",$_); 

    <Do something ....> 
} 

里面的解压模板中,“A ###”,你可以把该字段的宽度为每个A. 有多种其他格式,您可以使用混合和匹配,即整数字段等... 如果文件宽度固定,如大型机文件,那么这应该是最简单的。

6

CPAN来救援!

DataExtract::FixedWidth不仅分析固定宽度的文件,但(基于POD)似乎足够聪明,可以根据标题行自行计算列宽!