为什么我不能使用map函数从Perl中的简单数据文件创建一个好的哈希?

Mik*_*ke 3 perl hash byte-order-mark

帖子已更新.如果您已阅读已发布的问题,请转到解决方案部分.谢谢!

这是展示我的问题的最小化代码:

用于测试的输入数据文件已由Window的内置记事本保存为UTF-8编码.它有以下三行:

abacus  æb?k?s
abalone æb?l?uni
abandon ?bænd?n

Perl脚本文件也被Window的内置记事本保存为UTF-8编码.它包含以下代码:

#!perl -w

use Data::Dumper;
use strict;
use autodie;
open my $in,'<',"./hash_test.txt";
open my $out,'>',"./hash_result.txt";

my %hash = map {split/\t/,$_,2} <$in>;
print $out Dumper(\%hash),"\n";
print $out "$hash{abacus}";
print $out "$hash{abalone}";
print $out "$hash{abandon}";
Run Code Online (Sandbox Code Playgroud)

在输出中,哈希表似乎没问题:

$VAR1 = {
          'abalone' => 'æb?l?uni
',
          'abandon' => '?bænd?n',
          '?abacus' => 'æb?k?s
'
        };

但实际上并非如此,因为我只获得两个值而不是三个:

æb?l?uni
?bænd?n

Perl给出以下警告消息:

Use of uninitialized value $hash{"abacus"} in string at C:\test2.pl line 11, <$i n> line 3.

哪里有问题?有人可以解释一下吗?谢谢.

解决方案

数百万的感谢你们所有人:)现在终于找到了罪魁祸首,问题变得可以解决了:)正如@Sinan深刻地指出的那样,我现在100%确定导致上述问题的罪魁祸首是两个BOM的字节,当记事本保存为UTF-8时,记事本添加到我的数据文件中,并且Perl不知道如何正确处理.虽然很多人建议我应该使用"<:utf8"和">:utf8"来读写文件,但事情是这些utf-8配置并没有解决问题.相反,它们可能会导致其他一些问题

要真正解决问题,我实际需要的是添加一行代码以强制Perl忽略BOM:

#!perl -w

use Data::Dumper;
use strict;
use autodie;

open my $in,'<',"./hash_test.txt";
open my $out,'>',"./hash_result.txt";

seek $in,3,0; # force Perl to ignore the BOM!
my %hash = map {split/\t/,$_,2} <$in>;
print $out Dumper(\%hash);
print $out $hash{abacus};
print $out $hash{abalone};
print $out $hash{abandon};
Run Code Online (Sandbox Code Playgroud)

现在,输出正是我的预期:

$VAR1 = {
          'abalone' => 'æb?l?uni
',
          'abandon' => '?bænd?n',
          'abacus' => 'æb?k?s
'
        };
æb?k?s
æb?l?uni
?bænd?n

请注意,脚本保存为UTF-8编码,代码不必包含任何utf-8标签,因为输入文件和输出文件都预先保存为UTF-8编码.

最后再次感谢大家.感谢@Sinan的深刻指导.没有你的帮助,我会留在黑暗中,因为上帝知道多久.

注意 为了澄清一点,如果我使用:

open my $in,'<:utf8',"./hash_test.txt";
open my $out,'>:utf8',"./hash_result.txt";

my %hash = map {split/\t/,$_,2} <$in>;
print $out Dumper(\%hash);
print $out $hash{abacus};
print $out $hash{abalone};
print $out $hash{abandon};
Run Code Online (Sandbox Code Playgroud)

输出是这样的:

$VAR1 = {
          'abalone' => "\x{e6}b\x{4d9}l\x{4d9}uni
",
          'abandon' => "\x{4d9}b\x{e6}nd\x{4d9}n",
          "\x{feff}abacus" => "\x{e6}b\x{4d9}k\x{4d9}s
"
        };
æb?l?uni
?bænd?n

并警告信息:

Use of uninitialized value in print at C:\hash_test.pl line 13,  line 3.

Sin*_*nür 7

我发现警告信息有点可疑.它告诉你$in文件句柄在读取最后一行后应该在第4行的第3行.

当我尝试你的代码时,我使用在我的系统上配置的GVim保存输入文件以保存为UTF-8,我没有看到问题.现在我用记事本试了一下,查看输出文件,我看到:

"\x{feff}abacus" => "\x{e6}b\x{4d9}k\x{4d9}s
"

这里\x{feff}BOM.

在您的Dumper输出中,之前存在虚假空白abacus(您没有:utf8为输出句柄指定).

正如我之前提到的(在这篇文章中输入了无数编辑 - 感谢提醒hobbs),指定'<:utf8'何时打开输入文件.