在Perl中解析结构化文本文件

时间:2014-10-11 14:59:49

标签: perl

我是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"

或者那些东西。

到目前为止,我已经能够将所有字段名称自己变为哈希,但是我没有任何运气将字段数据转换为可以很好地存储在哈希中的表单。显然,替换/拆分新行后跟选项卡是行不通的(我试过,有点天真)。我还尝试了一个粗略的前瞻,我从文件中创建了一个重复的行数组,并使用它来确定字段边界的位置,但它在内存消耗方面并不是那么好。

FWIW,目前我正在逐行浏览文件,但我并不完全相信这是最好的解决方案。有没有办法以简单的方式进行解析?

2 个答案:

答案 0 :(得分:5)

逐行读取文件是一个很好的方法。这里我创建了一个数组引用的哈希。这就是你如何阅读一个文件。您可以通过这种方式读取每个文件,并将数组的哈希值放入数组哈希值的散列中。

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

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