如何解析perl中的多行固定宽度文件?

时间:2011-12-14 19:24:43

标签: perl parsing fixed-width

我有一个文件,我需要以下面的格式解析。 (所有分隔符都是空格):

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.

我熟悉如何解析单行固定宽度文件,但是我很难理解如何处理多行。

4 个答案:

答案 0 :(得分: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.

答案 1 :(得分: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'
          ]
        ];

答案 2 :(得分: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;

虽然这不是推荐的方法。

答案 3 :(得分:0)

您可以更改分隔符:

$/ = "\nfield name";

while (my $line = <FILE>) {

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