使用Perl解析结构化文本文件

3

我是一名Perl的新手,我很难编写一个能够成功解析结构化文本文件的Perl脚本。

我有一批长这样的文件:

name:
    John Smith
occupation:
    Electrician
date of birth:
    2/6/1961
hobbies:
    Boating
    Camping
    Fishing

等等。字段名称后面总是跟着一个冒号,与这些字段相关的所有数据总是缩进一个制表符 (\t)。

我想创建一个哈希表,可以直接将字段内容与字段名称关联起来,就像这样:

 $contents{$name} = "John Smith"
 $contents{$hobbies} = "Boating, Camping, Fishing"

或者类似这样的东西。

到目前为止,我已经成功将所有字段名称单独存储在哈希表中,但是我没有成功地将字段数据整理成可以很好地存储在哈希表中的形式。显然,替换/分割后跟制表符的换行符是行不通的(我尝试过,有点天真)。我还尝试了一种粗略的前瞻方法,在其中创建了一个文件的行的重复数组,并使用它来确定字段边界,但从内存消耗的角度来看,这并不是很好。

顺便说一下,目前我正在逐行处理文件,但我并不完全相信这是最佳解决方案。有没有什么简单直接的方法来进行此解析?

2个回答

6

逐行读取文件是一个不错的方法。这里我创建了一个数组引用的哈希表。这就是你只读取一个文件的方式。你可以按照这种方式读取每个文件,并将数组的哈希表放入另一个哈希表中。

#!/usr/bin/perl

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

my %contents;
my $key;
while(<DATA>){
    chomp;
    if ( s/:\s*$// ) {
        $key = $_;
    } else {
        s/^\s+//g; # remove extra whitespace
        push @{$contents{$key}}, $_;
    }
}
print Dumper \%contents;

__DATA__
name:
    John Smith
occupation:
    Electrician
date of birth:
    2/6/1961
hobbies:
    Boating
    Camping
    Fishing

输出:

$VAR1 = {
          'occupation' => [
                             'Electrician'
                           ],
          'hobbies' => [
                          'Boating',
                          'Camping',
                          'Fishing'
                        ],
          'name' => [
                       'JohnSmith'
                     ],
          'date of birth' => [
                                '2/6/1961'
                              ]
        };

2
最好不要使用s/\s+//g;删除所有额外的空格 - 它在名称中很有用! ;) - i alarmed alien
2
@ialarmedalien看起来Miller将其更新为前导空格。绝对会搞砸名称,做得好。只是想扔些东西在那里,以显示如果需要,您可以在元素上执行处理! - hmatt1
这正是我所需要的 - 比我想象中的要容易得多。谢谢! - MARS

2

这个文本文件实际上非常接近yaml格式。将其转换为有效的yaml文件并不困难:

一旦你有了一个yaml文件,你可以使用YAML::Tiny或其他模块来解析它,这会导致更清晰的代码:

#!/usr/bin/perl
use strict;
use warnings;

use YAML::Tiny;
use Data::Dumper;

convert( './data.yaml', 'output.yaml' );
parse('output.yaml');

sub parse {
    my $yaml    = shift;
    my $yamlobj = YAML::Tiny->read($yaml);

    my $name    = $yamlobj->[0]->{name}[0];
    my $occ     = $yamlobj->[0]{occupation}[0];
    my $birth   = $yamlobj->[0]{'date of birth'}[0];
    my $hobbies = $yamlobj->[0]{hobbies};

    my $hobbiestring = join ", ", @$hobbies;

    my $contents = {
        name       => $name,
        occupation => $occ,
        birth      => $birth,
        hobbies    => $hobbiestring,
    };

    print "#RESULT:\n\n";
    print Dumper($contents);
}

sub convert {
    my ( $input, $output ) = @_;

    open my $infh,  '<', $input  or die "$!";
    open my $outfh, '>', $output or die "$!";

    while ( my $line = <$infh> ) {
        $line =~ s/^\s+\K$/-/g;
        print $outfh ($line);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接