3

複数の区切り文字を持つ単一の文字列を key=>value ハッシュ構造に変換したいと考えています。これを達成する簡単な方法はありますか?私の現在の実装は次のとおりです。

sub readConfigFile() {
    my %CONFIG;
    my $index = 0;
    open(CON_FILE, "config");
    my @lines = <CON_FILE>;
    close(CON_FILE);

    my @array = split(/>/, $lines[0]);
    my $total = @array;

    while($index < $total) {
        my @arr = split(/=/, $array[$index]); 
        chomp($arr[1]);
        $CONFIG{$arr[0]} = $arr[1];       
        $index = $index + 1; 
    }

    while ( ($k,$v) = each %CONFIG ) {
        print "$k => $v\n";
    }

    return;
}

「config」には次が含まれます。

pub=3>rec=0>size=3>adv=1234 123 4.5 6.00
pub=1>rec=1>size=2>adv=111 22 3456 .76

最後の数字も削除する必要があり、「ip」という名前の別のキー=>値のペアに保持する必要があります。(コードを長く複雑にしなければ、これを達成することはできませんでした)。

4

5 に答える 5

4

構成データ構造はどのように見えるはずですか? これまでのところ、ソリューションは最後の行のみを記録します。これは、レコードを追加するたびに同じハッシュ キーを踏んでいるためです。

これはあなたを近づけるかもしれませんが、データ構造がどうあるべきかを理解する必要があります。

  • サブルーチンがデータを取得する特定の方法に結び付けられないように、ファイル ハンドルを引数として渡します。この場合、ファイル、文字列、ソケット、またはDATAの下のものからでもかまいません。

  • 文字列を解析した後で問題を修正する代わりに、文字列を解析する前に「ip」要素を含むように文字列を修正します。これを行うと、「ip」要素は特別なケースではなく、二重分割の問題になります。これは、多くの作業とコードを節約するための非常に重要な手法です。

  • サブルーチン内でハッシュ参照を作成し、完了したらそのハッシュ参照を返します。グローバル変数は必要ありません。:)

警告を使用します。
厳密に使用します。

Data::Dumper を使用します。

readConfigFile( \*DATA );

サブ readConfigFile
    {
    my( $fh ) = シフト;

    私の $hash = {};

    while( <$fh> )
        {
        チョップ;

        s/\s+(\d*\.\d+)$/>ip=$1/;

        $ハッシュ->{ $. } = { マップ { 分割 /=/ } 分割 />/ };
        }

    $ハッシュを返します。
    }

私の $hash = readConfigFile( \*DATA );

print Dumper( $hash );

__データ__
pub=3>rec=0>size=3>adv=1234 123 4.5 6.00
pub=1>rec=1>size=2>adv=111 22 3456 .76

これにより、各行が個別のレコードであるデータ構造が得られます。レコードの行番号 ( $.) を最上位キーとして選択しますが、好きなものを使用できます。

$VAR1 = {
          '1' => {
                   'ip' => '6.00',
                   'rec' => '0',
                   'adv' => '1234 123 4.5',
                   'パブ' => '3',
                   'サイズ' => '3'
                 }、
          '2' => {
                   'ip' => '.76',
                   'rec' => '1',
                   'adv' => '111 22 3456',
                   'パブ' => '1',
                   'サイズ' => '2'
                 }
        };

それがご希望の構造でない場合は、ご希望の構造をお知らせください。回答を調整いたします。

于 2008-11-08T20:14:22.283 に答える
2

1行以上を読んで解析したいと思っています。そこで、値を AoH に格納することにしました。

#!/usr/bin/perl
厳密に使用します。
警告を使用します。

私の @config;

while (<データ>) {
    チョップ;
    push @config, { 分割 /[=>]/ };
}

私の $href (@config) {
    while (my ($k, $v) = each %$href) {
        print "$k => $v\n";
    }
}

__データ__
pub=3>rec=0>size=3>adv=1234 123 4.5 6.00
pub=1>rec=1>size=2>adv=111 22 3456 .76

これにより、以下の印刷結果が得られます。(上記の while ループは DATA から読み取ります。)

記録 => 0
adv => 1234 123 4.5 6.00
パブ => 3
サイズ => 3
記録 => 1
adv => 111 22 3456 .76
パブ => 1
サイズ => 2

クリス

于 2008-11-08T20:21:28.340 に答える
1

設定ファイルのフォーマットは最適ではありません。つまり、解析して理解するのが簡単な形式があります。[追加: ただし、形式は別のプログラムによって既に定義されています。Perl はそれに対処するのに十分柔軟です。]

本当に必要がない場合、コードはファイルを丸呑みします。

あなたのコードは、ファイル内のデータの最後の行にのみ注意を払います (これを入力しているときに Chris Charley が指摘したように)。

また、コメント行や空白行も許可していません。どちらも構成ファイルで使用することをお勧めします。サポートも容易です。[追加: 繰り返しますが、定義済みの形式では、これはほとんど関係ありませんが、独自のファイルを設計するときは覚えておいてください。]

これは、関数をより慣用的な Perl に適応させたものです。

#!/bin/perl -w
use strict;
use constant debug => 0;

sub readConfigFile()
{
    my %CONFIG;
    open(CON_FILE, "config") or die "failed to open file ($!)\n";

    while (my $line = <CON_FILE>)
    {
        chomp $line;
        $line =~ s/#.*//;           # Remove comments
        next if $line =~ /^\s*$/;   # Ignore blank lines

        foreach my $field (split(/>/, $line))
        {
            my @arr = split(/=/, $field);
            $CONFIG{$arr[0]} = $arr[1];
            print ":: $arr[0] => $arr[1]\n" if debug;
        }
    }
    close(CON_FILE);

    while (my($k,$v) = each %CONFIG)
    {
        print "$k => $v\n";
    }
    return %CONFIG;
}

readConfigFile;    # Ignores returned hash

ここで、最後のフィールドの構造と、key=value 表記のない「ip」フィールドがある理由をより明確に説明する必要があります。一貫性は、誰にとっても生活を楽にします。また、複数行の処理方法についても考える必要があります。そして、次のようなより正統な表記法を使用して探索します。

pub=3;rec=0;size=3;adv=(1234,123,4.5);ip=6.00

区切り文字としてのコロンまたはセミコロンは、かなり一般的です。リスト内のコンマで区切られた項目を括弧で囲むことは、法外な規則ではありません。一貫性が最も重要です。エマーソンは、「愚かな一貫性は、ちっぽけな政治家や哲学者や神学者に愛される、ちっぽけな頭脳のホブゴブリンです」と述べていますが、コンピューター サイエンスにおける一貫性は、すべての人にとって大きな利益となります。

于 2008-11-08T20:36:21.070 に答える
1

以下では、区切り文字が > であることが保証されており、それがデータに現れる可能性がないことを前提としています。

「>」に基づいて各行を分割するだけです。最後の値にはキー=値のペア、スペース、IP が含まれるため、これを // ちょうど 1 回 (制限 2) 分割すると、k=v と IP が得られます。IP をハッシュに保存し、k=v ペアを配列に保持してから、配列を調べて「=」で k=v を分割します。

hashref を入力し、よりスコープの広い配列にプッシュします。これには、終了時にハッシュリファレンスが含まれます。

(構成を配列にロードした後)

my @hashes;

for my $line (@config) {
    my $hash; # config line will end up here

    my @pairs = split />/, $line;

    # Do the ip first. Split the last element of @pairs and put the second half into the
    # hash, overwriting the element with the first half at the same time.
    # This means we don't have to do anything special with the for loop below.
    ($pairs[-1], $hash->{ip}) = (split / /, $pairs[-1], 2);

    for (@pairs) {
        my ($k, $v) = split /=/;
        $hash->{$k} = $v;
    }

    push @hashes, $hash;
}
于 2008-11-10T12:03:34.037 に答える
0

これが1つの方法です。

foreach ( @lines ) {
  チョップ;
  私の%CONFIG;
  # 最初に最後の桁を抽出し、末尾に置き換えます
  # ペア区切り。
  s/\s*([\d\.]+)\s*$/>/;
  $CONFIG{ip} = $1;
  while ( /([^=]*)=([^>]*)>/g ) {
    $CONFIG{$1} = $2;
  }
  印刷ダンパー ( \%CONFIG );
}
于 2008-11-08T19:23:11.840 に答える