3

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

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

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

算盘 æbәkәs
鲍鱼 æbәlәuni
放弃ә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}";

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

$VAR1 = {
          '鲍鱼' => 'æbәlәuni
',
          '放弃' => 'әbændәn',
          '算盘' => 'æ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};

现在,输出正是我所期望的:

$VAR1 = {
          '鲍鱼' => 'æbәlәuni
',
          '放弃' => 'әbændәn',
          '算盘' => 'æ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};

输出是这样的:

$VAR1 = {
          '鲍鱼' => "\x{e6}b\x{4d9}l\x{4d9}uni
",
          '放弃' => "\x{4d9}b\x{e6}nd\x{4d9}n",
          "\x{feff}算盘" => "\x{e6}b\x{4d9}k\x{4d9}s
"
        };
æbәlәuni
әbændәn

和警告信息:

在 C:\hash_test.pl 第 13 行第 3 行的打印中使用未初始化的值。
4

5 回答 5

7

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

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

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

BOM\x{feff}在哪里。

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

正如我最初提到的(迷失在这篇文章的无数次编辑中——感谢霍布斯的提醒),请指定'<:utf8'您何时打开输入文件。

于 2009-11-19T13:03:55.460 回答
2

如果你想读/写 UTF8 文件,你应该确保你实际上是以UTF8的形式读入的。

#! /usr/bin/env perl
use Data::Dumper;
open my $in,  '<:utf8', "hash_test.txt";
open my $out, '>:utf8', "hash_result.txt";

my %hash = map { chomp; split ' ', $_, 2 } <$in>;
print $out Dumper(\%hash),"\n";
print $out "$hash{abacus}\n";
print $out "$hash{abalone}\n";
print $out "$hash{abandon}\n";

如果您希望它更健壮,建议使用:encoding(utf8)而不是:utf8, 来读取文件。

open my $in, '<:encoding(utf8)', "hash_test.txt";

阅读PerlIO了解更多信息。

于 2009-11-19T14:25:44.550 回答
1

我想你的答案可能就在你面前。您发布的输出Data::Dumper是:

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

'注意和之间的字符abacus?您试图通过 访问第三个值$hash{abacus}。这是不正确的,因为哈希之前abacus的那个字符。Dumper()您可以尝试将其插入应该处理它的循环中:

foreach my $k (keys %hash) {
  print $out $hash{$k};
}
于 2009-11-19T14:57:44.640 回答
0

split/\s/ 而不是 split/\t/

于 2009-11-19T12:42:03.773 回答
-1

为我工作。您确定您的示例与您的实际代码和数据匹配吗?

于 2009-11-19T13:09:05.750 回答