2011-12-14 64 views
4

我有一個文件,我需要解析以下格式。 (所有的分隔符是空格):如何解析perl中的多行固定寬度文件?

field name 1:   Multiple word value. 
field name 2:   Multiple word value along 
         with multiple lines. 
field name 3:   Another multiple word 
         and multiple line value. 

我熟悉如何解析一行固定寬度的文件,但我有如何處理多行難住了。

回答

8
#!/usr/bin/env perl 

use strict; use warnings; 

my (%fields, $current_field); 

while (my $line = <DATA>) { 
    next unless $line =~ /\S/; 

    if ($line =~ /^ \s+ (\S .+)/x) { 
     if (defined $current_field) { 
      $fields{ $current_field} .= $1; 
     } 
    } 
    elsif ($line =~ /^(.+?) : \s+ (.+) \s+/x) { 
     $current_field = $1; 
     $fields{ $current_field } = $2; 
    } 
} 

use Data::Dumper; 
print Dumper \%fields; 

__DATA__ 
field name 1:   Multiple word value. 
field name 2:   Multiple word value along 
         with multiple lines. 
field name 3:   Another multiple word 
         and multiple line value. 
+2

謝謝!我將`。+`的第一個實例更改爲`。+?`,以使模式匹配不成立。這幫助我使用包含「:」字符的值。 – NeonD 2011-12-14 21:44:38

2

你可以這樣做:

#!/usr/bin/perl 

use strict; 
use warnings; 

my @fields; 
open(my $fh, "<", "multi.txt") or die "Unable to open file: $!\n"; 

for (<$fh>) { 
    if (/^\s/) { 
     $fields[$#fields] .= $_;  
    } else { 
     push @fields, $_; 
    } 
} 

close $fh; 

如果一行以空格開始,其追加到@fields的最後一個元素,否則將其推到數組的末尾。

另外,啜整個文件和分裂與環視:

#!/usr/bin/perl 

use strict; 
use warnings; 

$/=undef; 

open(my $fh, "<", "multi.txt") or die "Unable to open file: $!\n"; 

my @fields = split/(?<=\n)(?!\s)/, <$fh>; 

close $fh; 

這不是一個推薦的方法,雖然。

4

固定寬度表示unpack給我。可以用正則表達式和分割進行解析,但unpack應該是更安全的選擇,因爲它是固定寬度數據的正確工具。

我把第一個字段的寬度設置爲12,將空白間隔設置爲13,這對於這些數據起作用。你可能需要改變它。模板"A12A13A*"的意思是「找到12,然後13個ASCII字符,後面是任意長度的ASCII字符」。 unpack將返回這些匹配的列表。另外,如果沒有提供字符串,unpack將使用$_,這是我們在這裏執行的操作。

請注意,如果第一個字段的寬度不是固定的,因爲它看起來在您的示例數據中,您需要合併模板中的字段,例如, 「A25A *」,然後剝離結腸。

我選擇數組作爲存儲設備,因爲我不知道您的字段名是否是唯一的。哈希將覆蓋具有相同名稱的字段。數組的另一個好處是它保留了數據在文件中出現的順序。如果這些東西無關緊要,而且快速查找更重要,則可以使用散列代替。

代碼:

use strict; 
use warnings; 
use Data::Dumper; 

my $last_text; 
my @array; 
while (<DATA>) { 
    # unpack the fields and strip spaces 
    my ($field, undef, $text) = unpack "A12A13A*"; 
    if ($field) { # If $field is empty, that means we have a multi-line value 
      $field =~ s/:$//;    # strip the colon 
     $last_text = [ $field, $text ]; # store data in anonymous array 
     push @array, $last_text;   # and store that array in @array 
    } else {  # multi-line values get added to the previous lines data 
     $last_text->[1] .= " $text"; 
    } 
} 

print Dumper \@array; 

__DATA__ 
field name 1:   Multiple word value. 
field name 2:   Multiple word value along 
         with multiple lines. 
field name 3:   Another multiple word 
         and multiple line value 
         with a third line 

輸出:

$VAR1 = [ 
      [ 
      'field name 1:', 
      'Multiple word value.' 
      ], 
      [ 
      'field name 2:', 
      'Multiple word value along with multiple lines.' 
      ], 
      [ 
      'field name 3:', 
      'Another multiple word and multiple line value with a third line' 
      ] 
     ]; 
0

您可以更改分隔符:

$/ = "\nfield name"; 

while (my $line = <FILE>) { 

    if ($line =~ /(\d+)\s+(.+)/) { 
     print "Record $1 is $2"; 
    } 
}