简体   繁体   English

Perl:将 hash 添加为子 hash 到简单 hash

[英]Perl: Add hash as sub hash to simple hash

I looked at the other two questions that seem to be about this, but they are a little obtuse and I can't relate them to what I want to do, which I think is simpler.我看了另外两个好像是关于这个的问题,但是有点生硬,我不能把它们和我想做的事情联系起来,我认为这更简单。 I also think this will be a much clearer statement of a very common problem/task so I'm posting this for the benefit of others like me.我也认为这将是一个非常常见的问题/任务的更清晰的陈述,所以我发布这个是为了像我这样的其他人的利益。

The Problem:问题:

I have 3 files, each file a list of key=value pairs:我有 3 个文件,每个文件都有一个key=value对列表:

settings1.ini设置1.ini

key1=val1
key2=val2
key3=val3

settings2.ini设置2.ini

key1=val4
key2=val5
key3=val6

settings3.ini设置3.ini

key1=val7
key2=val8
key3=val9

No surprise, I want to read those key=value pairs into a hash to operate on them, so...毫不奇怪,我想将那些key=value对读入 hash 以对它们进行操作,所以......

I have a hash of the filenames:我有一个 hash 的文件名:

my %files = { file1 => 'settings1.ini'
            , file2 => 'settings2.ini'
            , file3 => 'settings3.ini'
            };

I can iterate through the filenames like so:我可以像这样遍历文件名:

foreach my $fkey (keys %files) {
    say $files{$fkey};
}

Ok.行。

Now I want to add the list of key=value pairs from each file to the hash as a sub-hash under each respective 'top-level' filename key, such that I can iterate through them like so:现在我想将每个文件中的key=value对列表添加到 hash 作为每个相应“顶级”文件名键下的子哈希,这样我就可以像这样遍历它们:

foreach my $fkey (keys %files) {
    say "File: $files{$fkey}";
    foreach my $vkey (keys $files{$fkey}) {
        say "  $vkey: $files{$fkey}{$vkey}";
    }
}

In other words, I want to add a second level to the hash such that it goes from just being (in psuedo terms) a single layer list of values:换句话说,我想向 hash 添加第二个级别,这样它就不再只是(用伪术语)单层值列表:

file1 => settings1.ini
file2 => settings2.ini
file3 => settings3.ini

to being a multi-layered list of values:成为一个多层值列表:

file1 => key1 => 'val1'
file1 => key2 => 'val2'
file1 => key3 => 'val3'

file2 => key1 => 'val4'
file2 => key2 => 'val5'
file2 => key3 => 'val6'

file3 => key1 => 'val7'
file3 => key2 => 'val8'
file3 => key3 => 'val9'

Where:在哪里:

my $fkey = 'file2';
my $vkey  = 'key3';
say $files{$fkey}{$vkey};

would print the value将打印值

'val6'

As a side note, I am trying to use File::Slurp to read in the key=value pairs.作为旁注,我正在尝试使用File::Slurp读取key=value对。 Doing this on a single level hash is fine:在单个级别 hash 上执行此操作很好:

my %new_hash = read_file($files{$fkey}) =~ m/^(\w+)=([^\r\n\*,]*)$/img;

but - to rephrase this whole problem - what I really want to do is 'graft' the new hash of key=value pairs onto the existing hash of filenames 'under' the top $file key as a 'child/branch' sub-hash.但是-重新表述整个问题-我真正想做的是将新的 hash key=value对“嫁接”到现有的 hash 文件名“位于”顶部$file键下作为“子/分支”子哈希.

Questions:问题:

  • How do I do this, how do I build a multi-level hash one layer at a time like this?我该怎么做,如何像这样一次构建一个多层 hash 一层?
  • Can I do this without having to pre-define the hash as multi-layered up front?我可以在不必预先将 hash 预先定义为多层的情况下执行此操作吗?

I use strict;use strict; and so I have seen the所以我看到了

Can't use string ("string") as a HASH ref while "strict refs" in use at script.pl line <lineno>.

which I don't fully understand...我不完全明白...

Edit:编辑:

Thank you Timur Shtatland , Polar Bear and Dave Cross for your great answers.感谢Timur ShtatlandPolar BearDave Cross的出色回答。 In mentally parsing your suggestions it occurred to me that I had slightly mislead you by being a little inconsistent in my original question.在精神上分析你的建议时,我想到我在最初的问题中有点不一致而误导了你。 I apologize.我道歉。 I also think I see now why I saw the 'strict refs' error.我也想我现在明白为什么我看到“严格参考”错误了。 I have made some changes.我做了一些改变。

Note that my first mention of the initial hash of filename is correct .请注意,我第一次提到文件名的初始 hash是正确的。 The subsequent foreach examples looping through %files , however, were incorrect because I went from using file1 as the first file key to using settings1.ini as the first file key.然而,随后循环遍历%files的 foreach 示例是不正确的,因为我从使用file1作为第一个文件键改为使用settings1.ini作为第一个文件键。 I think this is why Perl threw the strict refs error - because I tried to change the key from the initial string to a hash_ref pointing to the sub-hash (or vice versa).我认为这就是 Perl 抛出strict refs错误的原因——因为我试图将密钥从初始字符串更改为指向子哈希的 hash_ref(反之亦然)。

Have I understood that correctly?我理解正确吗?

There are several CPAN modules purposed for ini files.有几个用于 ini 文件的 CPAN 模块。 You should study what is available and choose what your heart desire.你应该研究可用的东西并选择你内心想要的东西。

Otherwise you can write your own code something in the spirit of following snippet否则,您可以本着以下代码片段的精神编写自己的代码

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my @files = qw(settings1.ini settings2.ini settings3.ini);

my %hash;

for my $file (@files) {
    $hash{$file} = read_settings($file);
}

say Dumper(\%hash);

sub read_settings {
    my $fname = shift;
    my %hash;
    
    open my $fh, '<', $fname
        or die "Couldn't open $fname";
    
    while( <$fh> ) {
        chomp;
        my($k,$v) = split '=';
        $hash{$k} = $v
    }
    
    close $fh;
    
    return \%hash;
}

Output Output

$VAR1 = {
          'settings1.ini' => {
                               'key2' => 'val2',
                               'key1' => 'val1',
                               'key3' => 'val3'
                             },
          'settings2.ini' => {
                               'key2' => 'val5',
                               'key1' => 'val4',
                               'key3' => 'val6'
                             },
          'settings3.ini' => {
                               'key1' => 'val7',
                               'key2' => 'val8',
                               'key3' => 'val9'
                             }
        };

To build the hash one layer at a time, use anonymous hashes.要一次构建 hash 一层,请使用匿名哈希。 Each value of %files here is a reference to a hash, for example, for $files{'settings1.ini'} :此处%files的每个值都是对 hash 的引用,例如$files{'settings1.ini'}

# read the data into %new_hash, then:
$files{'settings1.ini'} = { %new_hash }

You do not need to predefine the hash as multi-layered (as hash of hashes) upfront.您不需要预先将 hash 预定义为多层(作为哈希的 hash)。

Also, avoid reinventing the wheel.另外,避免重新发明轮子。 Use Perl modules for common tasks, in this case consider something like Config::IniFiles for parsing *.ini files将 Perl 模块用于常见任务,在这种情况下,考虑像Config::IniFiles这样的东西来解析*.ini文件

SEE ALSO:也可以看看:

Anonymous hashes: perlreftut匿名哈希: perlreftut
Hashes of hashes: perldsc哈希的哈希值: perldsc

Perl makes stuff like this ridiculously easy. Perl 让这样的事情变得异常简单。

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';
use Data::Dumper;

my %files;

# <> reads from the files given on the command line
# one line at a time.
while (<>) {
  chomp;
  my ($key, $val) = split /=/;

  # $ARGV contains the name of the file that
  # is currently being read.
  $files{$ARGV}{$key} = $val;
}

say Dumper \%files;

Running this as:将其运行为:

$ perl readconf settings1.ini settings2.ini settings3.ini

Gives the following output:给出以下 output:

$VAR1 = {
          'settings3.ini' => {
                               'key2' => 'val8',
                               'key1' => 'val7',
                               'key3' => 'val9'
                             },
          'settings2.ini' => {
                               'key3' => 'val6',
                               'key1' => 'val4',
                               'key2' => 'val5'
                             },
          'settings1.ini' => {
                               'key3' => 'val3',
                               'key1' => 'val1',
                               'key2' => 'val2'
                             }
        };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM