4

我有一个 .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 这样的库,但我想避免使用任何库。有人可以帮我吗?

4

3 回答 3

6

对于低调的方法,您始终可以将键保存在一个数组中,该数组确实有一个顺序。

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}
}

但这确实存在问题,您依赖于键数组和哈希保持同步。如果您不小心,您也可能不小心多次添加相同的密钥。

于 2013-02-04T17:52:58.690 回答
4

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

于 2013-02-04T17:47:53.980 回答
3

哈希是一组具有唯一键的键值对。集合本身永远不会被订购。

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

这是我对您的问题的看法:

#!/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);在末尾(需要/c 正则表达式上的标志),以查看我们是否实际处理了所有内容或过早地被抛出循环。

这会产生:

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

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

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

于 2013-02-04T17:47:44.477 回答