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'                                                  
        }
      ]
];

(他のインデントを使用しますが、それは関係ありません)。

1つの落とし穴があります。ファイルは改行で終了する必要があります。そうしないと、最後のセミコロンがキャッチされません。

于 2013-02-04T17:47:44.477 に答える