4

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

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

4 回答 4

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.
于 2011-12-14T19:46:10.580 回答
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'
          ]
        ];
于 2011-12-15T00:08:28.423 回答
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,否则将其推到数组的末尾。

或者,slurp 整个文件并通过环视进行拆分:

#!/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;

不过,这不是推荐的方法。

于 2011-12-14T19:51:54.307 回答
0

You can change delimiter:

$/ = "\nfield name";

while (my $line = <FILE>) {

    if ($line =~ /(\d+)\s+(.+)/) {
        print "Record $1 is $2";
    }
}  
于 2011-12-15T16:32:19.310 回答