使用Perl填充哈希表中嵌套的哈希表

3

我有一个哈希嵌套的结构:

my %MacroA = ('Category' => {}, 'Item' => {}, 'Description' => {}, 'Score' => {});

我希望的是遍历文件并向不同的哈希表中添加新元素。假设行包含“layout”,我想每次看到它时将其存储在“Category”中。
我所做的是:
while (my $line = <$file>) {                                        

if ($line =~ /\b(layout)\b,/) { 
foreach my $categories (keys $MacroA{'Category'}) {
    $MacroA{Category} = $1;
}
}

在遇到包含“layout”的行后,%MacroA中的预期值是多少? - choroba
%MacroA = 'Category' => 'layout' @choroba%MacroA = '类别' => '布局' @choroba - El_Commandantee
2
你的问题包含的示例代码与http://stackoverflow.com/questions/13566521/hash-of-hashes-perl中的代码几乎完全相同。我不知道你是否是TheBlackCorsair用户,还是只是碰巧在同一个班级,但那篇帖子中的问题和答案应该会对你有所帮助。 - MattLBeck
5个回答

3
你的问题表述很混乱,但是你的问题似乎是因为你对引用而不是哈希进行了keys操作。新版本的Perl支持这种操作,但是你可能使用的是旧版本。
在你的示例中,$MacroA{'Category'}返回一个哈希引用,而不是哈希。你使用'Category' => {}初始化了哈希,并且{}是一个空匿名哈希的引用。
要将哈希引用转换为哈希,可以使用%{ ... }符号;在这种情况下,你需要写成keys %{ $MacroA{'Category'} }。是的,这看起来很丑陋,这就是为什么Perl更改以支持对引用进行keys操作。
但是请注意,你接下来的一行是$MacroA{Category} = $1;,它将引用替换为$1中的任何内容,可能是字符串"layout"。那不是一个引用,所以下次循环时,脚本会失败。你可能想要创建一个多级哈希表,例如$MacroA{Category}{$1} = $file或类似的东西,具体取决于你想在哈希表中构建的数据,但是你的目标并不是很清楚。另一个回答建议使用哈希数组,这可能是你想要的。在这种情况下,如果使用旧版本的Perl,则@{ ... }符号将数组引用转换为数组,你可以使用push操作。

@ Peter Corlett,我想要实现的是我有以下文件: - El_Commandantee
#类别,项目,说明,得分 布局,f.4,宏放置间隙,通过 布局,f.14,无区域拥堵,通过 布局,f.17,卡领二极管放置,不通过 布局,f.18,卡领缓冲区放置,通过 布局,f.26,接地连接,不通过 布局,f.28,允许CTS单元,不通过 布局,f.29,允许CTS层,通过 布局,f.31,时钟去耦电容,不通过 布局,f.33,时钟非默认规则,不通过我想将每行中的“布局”存储到“说明”中。 - El_Commandantee

2

看起来你想做一种命名列的事情。如果你知道如何做,这很简单。

从你对彼得的评论(“[我]想要将每行的“布局”存储到“描述”中”)可以看出,这段代码允许你存储它。

因为我看到哈希表的主要用途是记录本身,所以这个演示只使用了这个哈希表。它们被存储在一个数组中。我不太明白你想从你的例子中如何索引它们。而且你似乎有点困惑你想如何处理它们。当然,处理每一行中带有'layout'的内容,然后将其存储为“Category”字段并不是那么有用。

use strict;
use warnings;

my @columns = qw<field1 field2 field3 field4>;
my @list;
my $fh = \*::DATA;
my $header = <$fh>;
if ( substr( $header, 0, 1 ) eq '#' ) {
    ( $header ) = $header =~ m/#(.*)/;
    $header =~ s/\s+$//; 
    @columns = split /,\s*/, $header;
}
else { 
    seek( $fh, 0, 0 ); # go back
}

# optional statement to capitalize field names
@columns = map { ucfirst } @columns;

while ( my $line = <$fh> ) { 
    next unless $line =~ m/^\s*layout\b/;
    $line =~ s/\s*$//;
    # store fields by hash slice in the tip of the array
    @{ $list[@list] }{ @columns } = split /,\s*/, $line;
}

__DATA__
#category, item, description, score 
layout,f.4,Macro placement clearance,pass 
layout,f.14,No area congestion,pass 
layout,f.17,placement collar diode,fail 
layout,f.18,placement collar buffer,pass 
layout,f.26,tie connection,fail 
layout,f.28,CTS allowed cell,fail 
layout,f.29,CTS allowed layed,pass 
layout,f.31,Clock De-cap cell,fail 
layout,f.33,Clock non default rule,fail

虽然它并不能满足所有要求,但将记录复制到数组中是“处理它”的一个简单模型。我们可以尝试这样做:

my %by_item;
while ( my $line = <$fh> ) { 
    next unless $line =~ m/^\s*layout\b/;
    $line =~ s/\s*$//;
    my %h;
    @h{ @columns } = split /,\s*/, $line;
    $by_item{ $h{Item} } = \%h;
    ### OR 
    # push @{ $by_item{ $h{Item} } }, %h;
}

您也可以这样做:
my %by_field;
...
$by_field{Item}{ $h{Item} }               
    = $by_field{Description}{ $h{Description} } 
    = \%h
    ;

1

我认为你需要一个数组的哈希:

my %MacroA = ('Category' => [], 'Item' => [], 'Description' => [], 'Score' => []);

while (my $line = <$file>) {                                        

if ($line =~ /\b(layout)\b,/) { 
foreach my $categories (keys $MacroA{'Category'}) {
    push $MacroA{Category}, $1;
}
}

1
以下是一个完整的程序,分成不同的块。要运行它,请将答案复制并粘贴到名为populate的文件中,但删除此段落等注释部分。
几乎所有的Perl程序(特别是当你还是初学者时)都应该以此开始。
#! /usr/bin/env perl

use strict;
use warnings;

第一行告诉系统如何执行您的程序。启用strictwarnings pragma将有助于避免常见错误,并在您看到意外行为的情况下帮助解释程序正在做什么。
根据您的问题,您想要的数据结构是哈希数组。数组的每个“行”或元素将对应输入文件中的一行,并具有以下形式。
# { Category => '...', Item => '...', Description => '...', Score => '...' }

该程序还将从输入中读取列名。

代码使用Perl的“钻石操作符”来读取每一行输入。chomp会删除末尾的换行符(如果有的话)。

如果该行告诉我们标题名称(即以#开头),我们将每个字段存储在@columns中。ucfirst可能不太熟悉:它将字符串的第一个字符大写。因为有多个列名,我们使用mapucfirst应用于每个列名。

否则,该行代表数据行。我们将该行拆分成由逗号分隔的字段,并将它们加载到新哈希表中。push 行在 @MacroA 的末尾添加了一个引用(使用哈希表前面的反斜杠创建)。
my @MacroA;
my @columns;
while (<>) {
  chomp;

  if (s/^#//) {                               # / fix Stack Overflow coloring
    @columns = map ucfirst, split /\s*,\s*/;  # / ditto
  }
  else {
    my %row;
    @row{@columns} = split /,/;
    push @MacroA, \%row;
  }
}

请注意,上述拆分是na&iumlautve。对于处理一般的CSV输入,请使用CPAN上的CSV模块之一。 Data::Dumper模块可用于快速打印复杂数据结构的内容。将其放入您的调试工具包中。
use Data::Dumper;
$Data::Dumper::Indent = $Data::Dumper::Terse = 1;
print Dumper \@MacroA;

__END__

给定一个名为input的文件,其内容如下:

#category, item, description, score
layout,f.4,宏单元放置间隙,通过
layout,f.14,无区域拥堵,通过
layout,f.17,放置领口二极管,失败
layout,f.18,放置领口缓冲器,通过
layout,f.26,接地连接,失败
layout,f.28,允许CTS单元,失败
layout,f.29,允许CTS层,通过
layout,f.31,时钟去耦电容单元,失败
layout,f.33,时钟非默认规则,失败

以下是一个示例运行。

$ perl populate input
[
  {
    'Score' => '通过',
    'Item' => 'f.4',
    'Description' => '宏单元放置间隙',
    'Category' => '布局'
  },
  {
    'Score' => '通过',
    'Item' => 'f.14',
    'Description' => '无区域拥塞',
    'Category' => '布局'
  },
  {
    'Score' => '失败',
    'Item' => 'f.17',
    'Description' => '放置领口二极管',
    'Category' => '布局'
  },
  {
    'Score' => '通过',
    'Item' => 'f.18',
    'Description' => '放置领口缓冲器',
    'Category' => '布局'
  },
  {
    'Score' => '失败',
    'Item' => 'f.26',
    'Description' => '连接绑定',
    'Category' => '布局'
  },
  {
    'Score' => '失败',
    'Item' => 'f.28',
    'Description' => '允许CTS单元',
    'Category' => '布局'
  },
  {
    'Score' => '通过',
    'Item' => 'f.29',
    'Description' => '允许CTS层',
    'Category' => '布局'
  },
  {
    'Score' => '失败',
    'Item' => 'f.31',
    'Description' => '时钟去耦单元',
    'Category' => '布局'
  },
  {
    'Score' => '失败',
    'Item' => 'f.33',
    'Description' => '时钟非默认规则',
    'Category' => '布局'
  }
]

1

您的问题有些令人困惑。只需将“布局”添加到类别键中即可,无需涉及循环:

while (my $line = <$file>) {                                        
    if ($line =~ /\blayout\b,/) { 
        $MacroA{Category} = 'layout';
    }
}

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