Решено: доступ к контенту Ha sh несовместим с другой версией perl - PullRequest
1 голос
/ 15 марта 2020

Я столкнулся с интересной проблемой со следующим фрагментом кода в perl 5.22.1 и perl 5.30.0

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

#use Data::Dumper;

my %hash;
my %seen;
my @header = split ',', <DATA>;

chomp @header;

while(<DATA>) {
    next if /^\s*$/;
    chomp;
    my %data;
    @data{@header} = split ',';

    push @{$hash{person}}, \%data;
    push @{$hash{Position}{$data{Position}}}, "$data{First} $data{Last}";
    if( ! $seen{$data{Position}} ) {
        $seen{$data{Position}} = 1;
        push @{$hash{Role}}, $data{Position};
    }
}

#say Dumper($hash{Position});

my $count = 0;
for my $person ( @{$hash{person}} ) {
    say "Person: $count";
    say "Role: $person->{Position}";
}

say "---- Groups ----\n";

while( my($p,$m) = each %{$hash{Position}} ) {
    say "-> $p";
    my $members = join(',',@{$m});
    say "-> Members: $members\n";
}

say "---- Roles ----";

say '-> ' . join(', ',@{$hash{Role}});

__DATA__
First,Last,Position
John,Doe,Developer
Mary,Fox,Manager
Anna,Gulaby,Developer

Если код работает как есть - все работает нормально

Теперь достаточно добавить $count++ приращение, как показано ниже, и код выдаст ошибки

my $count = 0;
for my $person ( @{$hash{person}} ) {
    $count++;
    say "Person: $count";
    say "Role: $person->{Position}";
}

Ошибки:

Error(s), warning(s):
Use of uninitialized value $data{"Position"} in hash element at source_file.pl line 22, <DATA> line 2.
Use of uninitialized value $data{"Position"} in hash element at source_file.pl line 23, <DATA> line 2.
Use of uninitialized value $data{"Position"} in hash element at source_file.pl line 24, <DATA> line 2.
Use of uninitialized value $data{"Position"} in hash element at source_file.pl line 22, <DATA> line 3.
Use of uninitialized value $data{"Position"} in hash element at source_file.pl line 23, <DATA> line 3.
Use of uninitialized value $data{"Position"} in hash element at source_file.pl line 22, <DATA> line 4.
Use of uninitialized value $data{"Position"} in hash element at source_file.pl line 23, <DATA> line 4.
Use of uninitialized value in concatenation (.) or string at source_file.pl line 35, <DATA> line 4.
Use of uninitialized value in concatenation (.) or string at source_file.pl line 35, <DATA> line 4.
Use of uninitialized value in concatenation (.) or string at source_file.pl line 35, <DATA> line 4.
Use of uninitialized value in join or string at source_file.pl line 48, <DATA> line 4.

Эта проблема не проявляется в perl 5.30.0 (Windows 10, Strawberry Perl) или Perl v5.24.2 .

Примечание: проблема проявляется не только с $count++, но и с любым другим доступом к содержимому га sh рядом с say "Person: $count"; - post # 60653651

Я хотел бы услышать комментарии к этой ситуации, в чем причина?

ПРИЧИНА: входные данные имеют eol в форме DOS \r\n и при обработке данных в Linux chomp удаляется только \n, оставляя \r как часть имени поля (используется как ключ ha sh). Спасибо Шон за указание на источник проблемы.

РЕШЕНИЕ: универсальное исправление реализовано в виде подпрограммы snip_eol($arg)

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

my $debug = 0;

say "
Perl:  $^V
OS: $^O
-------------------
" if $debug;

my %hash;
my %seen;
my @header = split ',', <DATA>;

$header[2] = snip_eol($header[2]);        # problem fix

while(<DATA>) {
    next if /^\s*$/;

    my $line = snip_eol($_);              # problem fix

    my %data;
    @data{@header} = split ',',$line;

    push @{$hash{person}}, \%data;
    push @{$hash{Position}{$data{Position}}}, "$data{First} $data{Last}";
    if( ! $seen{$data{Position}} ) {
        $seen{$data{Position}} = 1;
        push @{$hash{Role}}, $data{Position};
    }
}

#say Dumper($hash{Position});

my $count = 0;
for my $person ( @{$hash{person}} ) {
    $count++;
    say "-> Name:   $person->{First} $person->{Last}";
    say "-> Role:   $person->{Position}\n";
}

say "---- Groups ----\n";

while( my($p,$m) = each %{$hash{Position}} ) {
    say "-> $p";
    my $members = join(',',@{$m});
    say "-> Members: $members\n";
}

say "---- Roles ----";

say '-> ' . join(', ',@{$hash{Role}});

sub snip_eol {
    my $data = shift;                      # problem fix

    #map{ say "$_ => " . ord } split '', $data if $debug;
    $data =~ s/\r// if $^O eq 'linux';
    chomp $data;
    #map{ say "$_ => " . ord } split '', $data if $debug;

    return $data;
}

__DATA__
First,Last,Position
John,Doe,Developer
Mary,Fox,Manager
Anna,Gulaby,Developer

1 Ответ

4 голосов
/ 15 марта 2020

Я могу повторить это поведение путем (Вкл. linux), сначала преобразовав исходный файл, чтобы иметь Windows -тиль \r\n окончания строк, а затем попытавшись запустить его. Таким образом, я подозреваю, что при тестировании различных версий вы иногда используете Windows, а иногда Linux / Unix и не конвертируете окончания строк файла соответствующим образом.

@chomp удаляет только символ новой строки (ну, текущее значение $/ должно быть pedanti c), поэтому при использовании в строке со строкой стиля Windows, заканчивающейся в нем, он оставляет возврат каретки. Ключ ha sh - это не "Position", а "Position\r", а это не то, что использует остальная часть вашего кода.

...