如何在Perl中保留哈希的顺序?

时间:2013-02-04 17:17:46

标签: perl hash

我有一个.sql文件,我正在阅读我的输入。假设该文件包含以下输入....

Message Fruits Fruit="Apple",Color="Red",Taste="Sweet";

Message Flowers Flower="Rose",Color="Red";

现在我编写了一个perl脚本来从这个文件生成哈希..

use strict;
use Data::Dumper;

if(open(MYFILE,"file.sql")){
    my @stack;
    my %hash;
    push @stack,\%hash;
    my @file = <MYFILE>;
    foreach my $row(@file){
        if($row =~ /Message /){
            my %my_hash;
            my @words = split(" ",$row);
            my @sep_words = split(",",$words[2]);

            foreach my $x(@sep_words){
                my($key,$value) = split("=",$x);
                $my_hash{$key} = $value;
            }
            push @stack,$stack[$#stack]->{$words[1]} = {%my_hash};
            pop @stack;
        }
    }
    print Dumper(\%hash);
}

我得到以下输出..

$VAR1 = {
          'Flowers' => {
                         'Flower' => '"Rose"',
                         'Color' => '"Red";'
                       },
          'Fruits' => {
                        'Taste' => '"Sweet";',
                        'Fruit' => '"Apple"',
                        'Color' => '"Red"'
                      }
        };

现在这里的哈希不保留读取输入的顺序。我希望我的哈希与输入文件的顺序相同。 我找到了像Tie :: IxHash这样的库,但是我想避免使用任何库。任何人都可以帮我解决???

3 个答案:

答案 0 :(得分:4)

对于低密钥方法,您可以始终将密钥保存在数组中,该数组确实有订单。

foreach my $x(@sep_words){
    my($key,$value) = split("=",$x);
    $my_hash{$key} = $value;
    push(@list_keys,$key);
}

然后提取,遍历键

foreach my $this_key (@list_keys) {
    # do something with $my_hash{$this_key}
}

但确实存在这个问题,你依赖于密钥数组和散列保持同步。如果您不小心,也可能会多次意外添加相同的密钥。

答案 1 :(得分:3)

Joel说得对 - 你无法可靠地信任Perl中哈希的顺序。如果您需要某个订单,则必须将您的信息存储在一个数组中。

答案 2 :(得分:2)

哈希是一组具有唯一键的键值对。套装本身从不订购。

数组是任意数量标量的序列。数组本身是有序的,但必须在外部强制执行唯一性。

以下是我对你的问题的看法:

#!/usr/bin/perl

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

local $/ = ";\n";

my @messages;

while (<DATA>) {
    chomp;
    my ($msg, $to, $what) = split ' ', $_, 3; # limit number of fragments.
    my %options;
    while($what =~ /(\w+) = "((?:[^"]++|\\.)*)" (?:,|$)/xg) {
        $options{$1} = $2;
    }
    push @messages, [$to => \%options];
}

print Dumper \@messages;

__DATA__
Message Fruits Fruit="Apple",Color="Red",Taste="Sweet";
Message Flowers Flower="Rose",Color="Red";

我将消息放入数组中,因为它必须进行排序。另外,我不会做一些我不需要的奇怪的体操。

我不会拆分所有换行符,因为您可以使用包含换行符的引用值。出于同样的原因,我不会盲目地分开,=并使用合理的正则表达式。可能值得添加错误检测,例如最后的die if not defined pos $what or pos($what) != length($what);(在regex上需要/c标志),以查看我们是否实际处理了所有内容或者是否过早地抛出循环。

这会产生:

$VAR1 = [
      [ 'Fruits',
        {
          'Taste' => 'Sweet',
          'Fruit' => 'Apple',
          'Color' => 'Red'
        }
      ],
      [ 'Flowers',
        {                                                                   
          'Flower' => 'Rose',                                               
          'Color' => 'Red'                                                  
        }
      ]
];

(与其他缩进,但这无关紧要)。

存在一个问题:文件必须以换行符终止,否则最后一个分号不会被捕获。