2

ファイルテキストを解析してからハッシュに入れたいと思います。私のファイルは次のようになります:

key1 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val
key2 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val
key3 val
key4 val,val
key5 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val

キーはスペースの前にあり、値はスペースの後と各コンマの前の要素のリストです。値が数行続くため、キーがない行がいくつかあります。

だから私はそのようなハッシュが欲しいです(私はPythonに最も精通しています):

hash={'key1':[val,val,...],'key2':[val,val,...]} 

私のコード: `

my %hashNames;
open INFILE, "./file.txt" or die $!;
my @temp = ();

while (my $line = <INFILE>)
{

    my @names = split /[\t,]/, $line;
    my $ID = $names[0];
    if ( $line =~ /\t/ )
    {

        my @temp=();
        for (my $i = 1; $i < @names; $i +=1)
        {
            push (@temp, $names[$i]);
        }

    }
    else
    {   

        for (my $i = 0; $i < @names; $i +=1)
        {
            push (@temp, $names[$i]);
        }       
    }
}`
4

5 に答える 5

3

あなたの問題は、改行があなたの記録をもはや分離しないということです。したがって、これを処理する方法は、無効なデフォルトの入力レコードセパレータを無効にして$/、有効なものをエミュレートすることです。

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

my %hash;
my $file;
{
    local $/;         # disable input record separator
    $file = <DATA>;   # entire file here now!
}

for my $line (split /^(?=\S+ )/m, $file) {  # records begin this way now
    $line =~ s/\n//g;                       # remove newlines
    my ($key, $val) = split ' ', $line, 2;  # divide into two fields
    $hash{$key} = [ split /,/, $val ];      # store the data
}

print Dumper \%hash;

__DATA__
key1 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val
key2 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val
key3 val
key4 val,val
key5 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val

説明:

  • 修飾子を/^(?=\S+ )/m使用して分割すると、文字列内の改行が一致するようになり、入力レコードの区切り文字がエミュレートされます。/m^
  • 文字列を2つのフィールドに分割するには、LIMIT2をに追加します。split
  • [ ... ]内部にsplitステートメントを含む匿名配列を使用して、ハッシュに直接分割します。
于 2013-03-18T12:56:16.607 に答える
2

Parse::RecDescentモジュールの使用

#! /usr/bin/env perl

use strict;
use warnings;

use Parse::RecDescent;

our %hash;
my $p = Parse::RecDescent->new(q!
  hash: entry(s?)
  entry: key value(s /,/)  { $::hash{$item[1]} = [ @{ $item[2] } ] }
  key: /\S+/
  value: /([^,\n]|\\,])+/
!);
die "$0: failed to create parser" unless defined $p;

my $text = do {{ local $/; <DATA> }};
$p->hash($text) or die "$0: parse failed";

for (sort keys %hash) {
  print "$_ => val x ", scalar @{ $hash{$_} }, "\n";
}

__DATA__
key1 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val
key2 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val
key3 val
key4 val,val
key5 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val

出力:

key1 => val x 22
key2 => val x 22
key3 => val x 1
key4 => val x 2
key5 => val x 52
于 2013-03-18T13:53:54.023 に答える
1

ここでの難しさは、レコードが「コンマが前に付いていない改行」で終了することです。残念ながら、入力レコード区切り文字$/を正規表現に設定することはできません。これにより、3つの快適なソリューションが残ります。

  1. ファイル全体をメモリにロードします。後でハッシュに同じ量の情報があるので、これは思ったほど悪くはありません。その後split /(?<!,)\n/、実際のレコードを取得できます。

    my %hash = do {
      local $/; # set to undef, for slurp
      map {
        my ($key, $vals) = split /\s+/, $_, 2; # split on first whitespace, into two strings
        $key => [ split /\s*,\s*/, $vals ];    # return a list of a key and a value array
      } split /(?<!,)\n/, <FILE>;              # split the file into records
    };
    
  2. readline入力をバッファリングし、正規表現で行を終了できる代替を記述できます。

  3. 末尾のコンマは行継続文字と考えることができます。

    my %hash;
    while(<FILE>) {
      $_ .= <FILE> while /,\n\z/;
      my ($key, $value) = split /\s+/, $_, 2;
      push @{ $hash{$key} }, split /\s*,\s*/, $value; # allow multiple occurrences of one key, simply append values to list.
    }
    
于 2013-03-18T13:00:52.540 に答える
0

ここに行く:

my %results;
my $key;
while(my $line = <INFILE>) {
    chomp($line);
    my @items = split(/, */, $line);
    $key = shift @items;
    $results{$key} = \@items;
}

あなたのステートメントを除いて、これは単純なケースで機能します:

値が数行続くため、キーがない行がいくつかあります。

ただし、これを処理するには、次の行がキーであるか値であるかを検出する方法を説明する必要があります。知っている場合は、それをifステートメントに入れ、前のキーを使用して新しい値をハッシュに追加できます。

my %results;
my $key;
while(my $line = <INFILE>) {
    chomp($line);
    my @items = split(/, */, $line);
    my $tmpkey = shift @items;
    if (is_real_key($tmpkey)) {
        $key = shift @items;
        $results{$key} = \@items;
    } else {
        push (@{$results{$key}}, $tmpkey, @items);
    }
}
于 2013-03-18T12:55:54.327 に答える
0
#!/usr/bin/perl

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

use Data::Dumper;

my $res_hash = {};
my ($current_key, $values);
my $push_again;
while ( my $line = <DATA>) {
  chomp $line;
  push ( @{ $res_hash->{$current_key} }, split(/,/, $values) ) if ( $current_key and $values and ( index($line, ' ') > 0) );
  if ( index($line, ' ') > 0 ){
    $push_again = 0;
    ($current_key, $values) = split( /\s/, $line);    
  } else {
    $values .= $line;
    $push_again = 1;
  }

};
push ( @{ $res_hash->{$current_key} }, split(/,/, $values) ) if $push_again;

say "result:".Dumper($res_hash);



__DATA__
key1 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val
key2 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val
key3 val
key4 val,val
key5 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val
于 2013-03-18T13:08:01.120 に答える