已解决:哈希内容访问与不同的perl版本不一致 [英] SOLVED: Hash content access is inconsistent with different perl version
问题描述
我在 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
如果代码按原样运行-一切正常
If the code run as it is -- everything works fine
现在,在波纹管中添加$count++
增量就足够了,并且代码会产生错误
Now it is sufficient to add $count++
increment as bellow and code produces errors
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 .
This problem does not manifest itself in perl 5.30.0 (Windows 10, Strawberry Perl) or Perl v5.24.2.
注意:问题不仅通过$count++
出现,而且以其他任何方式访问say "Person: $count";
-
Note: the problem manifests itself not only with $count++
but with any other access to content of the hash next to say "Person: $count";
-- post# 60653651
我想听听关于这种情况的评论,原因是什么?
I would like to hear comments on this situation, what is the cause?
原因::输入数据以DOS形式\r\n
出现 eol ,并且在 Linux chomp
中处理的数据仅删除\n
将\r
保留为字段名称的一部分(用作哈希键).感谢肖恩指出问题的根源.
CAUSE: input data have eol in DOS form \r\n
and when data processed in Linux chomp
removes only \n
leaving \r
as part of the field name (used as hash key). Thanks goes to Shawn for pointing out the source of the issue.
解决方案:通用修复以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
推荐答案
我可以通过(在Linux上)首先将源文件转换为具有Windows样式的\r\n
行尾,然后尝试运行它来复制此行为.因此,我怀疑在测试各种版本时,有时会使用Windows,而在其他时候则使用Linux/Unix,并且没有正确转换文件的行尾.
I can replicate this behavior by (On linux) first converting the source file to have Windows-style \r\n
line endings and then trying to run it. I thus suspect that in your testing of various versions you're using Windows sometimes, and a Linux/Unix other times, and not converting the file's line endings appropriately.
@chomp
仅删除换行符(Well,$/
的当前值将是多余的),因此,在以Windows样式行结尾的字符串上使用时,它将保留回车符.哈希键不是"Position"
,而是"Position\r"
,这不是其余代码使用的键.
@chomp
only removes a newline character (Well, the current value of $/
to be pedantic), so when used on a string with a Windows style line ending in it, it leaves the carriage return. The hash key is not "Position"
, it's "Position\r"
, which is not what the rest of your code uses.
这篇关于已解决:哈希内容访问与不同的perl版本不一致的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!