【问题标题】:Parse text file into nested data structure将文本文件解析为嵌套数据结构
【发布时间】:2018-05-15 00:12:51
【问题描述】:

这是我的文本文件:

animal, cola, husband, 36 
animal, wilma, wife, 31
animal, pebbles, kid, 4
brutal, george, husband, 41
brutal, jane, wife, 39
brutal, elroy, kid, 9
cosa, homer, husband, 34
cosa, marge, wife, 37
cosa, bart, kid, 11

这就是我想要的数据结构:

%HASH = (
    animal  => [
        { name => "cola",    role => "husband", age  => 36, },
        { name => "wilma",   role => "wife",    age  => 31, },
        { name => "pebbles", role => "kid",     age  =>  4, },
    ],
    brutal  => [
        { name => "george",  role => "husband", age  => 41, },
        { name => "jane",    role => "wife",    age  => 39, },
        { name => "elroy",   role => "kid",     age  =>  9, },
    ],
    cosa  => [
        { name => "homer", role => "husband", age => 34, },
        { name => "marge", role => "wife",    age => 37, },
        { name => "bart",  role => "kid",     age => 11, },
    ],
);

我有一些代码,但我无法将它们组合成一个连贯的脚本。我只想有人帮我定义这个结构并理解它。

【问题讨论】:

  • 输入文件是否真的有不同长度的缩进和分隔数据的空行?
  • 输入文件是CSV吗?如果是这样,请不要自己解析。请改用Text::CSV。 metacpan.org/pod/Text::CSV

标签: perl data-structures


【解决方案1】:
  1. 将每一行解析为一个散列。
  2. 从散列中删除键列。
  3. 根据键列将哈希推送到数组中。

代码:

use strict;
use warnings;

use Data::Dumper;

my %hash;
my @columns = qw(category name role age);

while (<DATA>) {
    chomp;
    my %temp;
    @temp{@columns} = split(/\s*,\s*/);

    my $key = delete($temp{category});
    push(@{$hash{$key}}, \%temp);
}

print Dumper(\%hash);

__DATA__
animal, cola, husband, 36
animal, wilma, wife, 31
animal, pebbles, kid, 4
brutal, george, husband, 41
brutal, jane, wife, 39
brutal, elroy, kid, 9
cosa, homer, husband, 34
cosa, marge, wife, 37
cosa, bart, kid, 11

输出:

$VAR1 = {
          'cosa' => [
                      {
                        'name' => 'homer',
                        'age' => '34',
                        'role' => 'husband'
                      },
                      {
                        'name' => 'marge',
                        'age' => '37',
                        'role' => 'wife'
                      },
                      {
                        'name' => 'bart',
                        'age' => '11',
                        'role' => 'kid'
                      }
                    ],
          'brutal' => [
                        {
                          'name' => 'george',
                          'age' => '41',
                          'role' => 'husband'
                        },
                        {
                          'name' => 'jane',
                          'age' => '39',
                          'role' => 'wife'
                        },
                        {
                          'name' => 'elroy',
                          'age' => '9',
                          'role' => 'kid'
                        }
                      ],
          'animal' => [
                        {
                          'name' => 'cola',
                          'age' => '36',
                          'role' => 'husband'
                        },
                        {
                          'name' => 'wilma',
                          'age' => '31',
                          'role' => 'wife'
                        },
                        {
                          'name' => 'pebbles',
                          'age' => '4',
                          'role' => 'kid'
                        }
                      ]
        };

【讨论】:

    【解决方案2】:

    您展示的数据结构有一个关键规则:散列中的值只能是标量。

    因此,要将多值变量与键关联,请使用对该变量的引用,此处为 arrayref。如果该数组中的值需要比标量更复杂,您再次使用引用,这里是一个 hashref。† 所以每个键的值都是一个 arrayref,其元素是 hashrefs。

    然后您需要学习如何访问结构中更深层次的元素。这也不是很复杂:在每个级别取消引用它们,然后您可以像使用数组或哈希一样使用它们。

    所有这些都在perldsc 中,需要使用perlreftut 清楚地说明。参考是perlref。

    当它用于您的问题时

    use warnings;
    use strict;
    
    use Data::Dump qw(dd);
    
    my $file = 'data.txt';
    open my $fh, '<', $file or die "Can't open $file: $!";
    
    my %result;
    
    while (<$fh>) {
        chomp;
        my ($key, $name, $role, $age) = split /\s*,\s*/;
    
        push @{$result{$key}}, 
             { name => $name, role => $role, age => $age };   
    }
    
    dd \%result;
    

    这会打印出正确的数据结构。我用Data::Dump看到了一个复杂的数据结构,需要安装;核心中有Data::Dumper。还有其他的。

    上面的split 使用正则表达式/\s*,\s*/ 作为分隔符,因此用逗号分隔行,可选地用空格包围。要拆分的字符串的默认值为$_。

    请注意,我们不必在使用它们之前“添加键”或使其 arrayref-value,因为这是通过 autovivification 完成的。参见例如 this page 和 this page 和 this page。 这是一个复杂的功能,如果使用不当可能会受到影响,因此请仔细阅读。


    † 如果我们尝试对数组元素使用数组或散列变量,我们实际上是在尝试将值列表放入数组的单个“槽”中。这当然是做不到的,发生的情况是它们会被“扁平化”——它们的元素与所有其他给定的标量元素合并,整个聚合列表填充数组

    my @ary = 1..3;
    my %hash = (a => 10, b => 20);
    # Most likely an error:
    my @all = (5, @ary, %hash, 100);  #--> (5, 1, 2, 3, a, 10, b, 20, 100)
    

    因为哈希本质上是无序的,所以键值对可以按任何顺序出现。

    相反,我们引用数组和哈希并写入

    my @all = (5, \@ary, \%hash, 100);
    

    由于引用是标量,它们是数组的合法元素,不会发生扁平化。所以现在@ary和%hash的内容保持了他们的个性,可以根据需要恢复。

    【讨论】:

      猜你喜欢
      • 2015-01-18
      • 1970-01-01
      • 2018-04-02
      • 1970-01-01
      • 1970-01-01
      • 2017-10-26
      • 2019-06-28
      • 2015-07-26
      • 1970-01-01
      相关资源
      最近更新 更多