Вставка и перебор хеша массивов

У меня есть следующие входные данные

Country1:operator1
Country1:operator2
Country1:operator3

Country2:operator1
Country2:operator2
Country2:operator3

Я хотел бы вставить эти данные в хэш %INFO так что каждый ключ соответствует массиву "операторов", поэтому я мог бы выполнить итерацию следующим образом

foreach $i ( keys %INFO ) {

    foreach $operator ( $INFO{$i} ) {
        print " $i ---> $operator \n";  
    }
}

Вот мое собственное решение, которое не работает должным образом

open($fh, "$info_file");

while (my $row = <$fh>) {
    chomp $row;
    @tokens = split(":",$row);

    $name     = $tokens[0];
    $operator = $tokens[2];

    if ($name =~ /^[A-Z]/) {

        if ( exists $INFO{$name} ) {
            $ptr = \$INFO{$name};
            push(@ptr, $operator);
        }
        else {
            @array = ( "$operator" );
            $INFO{$name} = [ @array ];
        }
    }
}

close($f);

3 ответа

Решение

Боюсь, ты слишком усложняешь.

open my $fh, '<', $info_file or die "Can't open '$info_file': $!";

my %info;

while (<$fh>) {
  next unless /^[A-Z]/;
  chomp;
  my ($name, $operator) = split /:/;
  push @{ $info{$name} }, $operator;
}

И чтобы получить к нему доступ:

foreach my $i (keys %info) {
  foreach my $op (@{ $info{$i} }) {
    say "$i ----> $op";
  }
}

Если вы обрабатываете значение хеш-функции так, как будто это ссылка на массив, то Perl сделает его ссылкой на массив.

См. Поваренную книгу Perl Data Structures для более подробной информации.

Хорошо, во-первых, в этом коде много ошибок, используйте три входных формы open и не заключайте в кавычки переменные по умолчанию, Perl знает, когда все должно быть строками. Так open($fh,"$info_file"); должно быть open($fh, '<', $info_file);

Второй разделитель не возвращает символ разделителя по умолчанию, поэтому $operator = $tokens[2]; должно быть $operator = $tokens[1];

В-третьих, почему вы игнорируете страны, которые не начинаются с А до Я в вашем файле?

Четвертое использование авто вивификации, так что весь if else блок можно заменить на push @{$INFO{$name}}, $operator

пятый $ptr а также @ptr являются отдельными переменными, присваивающими ссылку на массив $ptr не делает его доступным в @ptr также \$INFO{$name} принимает ссылку на то, что когда-либо $INFO{$name} есть, который в вашем случае уже является ссылкой на массив, так что вы получаете ссылку на ссылку на массив в $ptr это должно было быть написано $ptr = $INFO{$name}; если вы сохраняете этот код.

Шестое присваивание @array является избыточным, две строки в предложении else должны были быть написаны $INFO{$name} = [ $operator ];

Со всеми этими изменениями вы получаете

open($fh, '<', "$info_file");
while (my $row = <$fh>) {
    chomp $row;
    my @tokens = split(":",$row);
    $name = $tokens[0];
    $operator = $tokens[1];
    if ($name =~ /^[A-Z]/) {
        push @{$INFO{$name}}, $operator;
    }
}
close($f);

Вы очень близки, но нет необходимости явно использовать ссылки или инициализировать значение хеш-функции в пустой массив

Это все что тебе нужно

use strict;
use warnings 'all';

open my $fh, '<', $info_file or die $!;

my %info;

while ( <$fh> ) {
    chomp;
    my ($name, $operator) = split /:/;
    push @{ $info{$name} }, $operator if $name =~ /^[A-Z]/;
}
Другие вопросы по тегам