utf-8 - 在 Perl 中使用带有标量和 I/O 层的 open() 时编码的不确定性

标签 utf-8 character-encoding perl perl-io

几个小时以来,我一直在与我的 Perl 程序中的错误作斗争。我不确定是我做错了什么还是解释器做错了,但是代码是不确定的,而它应该是确定的,IMO。它还在古老的 Debian Lenny (Perl 5.10.0) 和刚刚升级到 Debian Wheezy (Perl 5.14.2) 的服务器上表现出相同的行为。它归结为这段 Perl 代码:

#!/usr/bin/perl
use warnings;
use strict;
use utf8;
binmode STDOUT, ":utf8";
binmode STDERR, ":utf8";
my $c = "";
open C, ">:utf8", \$c;
print C "š";
close C;
die "Does not happen\n" if utf8::is_utf8($c);
print utf8::decode($c) ? "Decoded\n" : "Undecoded\n";

它在启用警告的严格模式下初始化 Perl 5 解释器,使用字符串(而不是字节字符串)和以 UTF8 编码的命名标准流(UTF-8 的内部概念,但非常接近;更改为完整的 UTF-8 没有区别)。然后它打开一个“内存文件”(标量变量)的文件句柄,向其中打印一个两字节的 UTF-8 字符,并在关闭时检查变量。

标量变量现在总是翻转 UTF8 位。然而,它有时包含一个字节字符串(通过 utf8::decode() 转换为字符串),有时包含一个只需要翻转其 UTF8 位( Encode::_utf8_on() )的字符串。

当我重复执行我的代码(1000 次,通过 Bash)时,它会打印 UndecodedDecoded具有大致相同的频率。当我更改字符串时,我会写入"file",例如在末尾添加一个换行符,Undecoded消失。当utf8::decode成功,我在循环中尝试使用相同的原始字符串,它在同一个解释器实例中一直成功;然而,如果它失败了,它就会一直失败。

对观察到的行为的解释是什么?如何将文件句柄与字符串一起使用标量变量?

Bash游乐场:
for i in {1..1000}; do perl -we 'use strict; use utf8; binmode STDOUT, ":utf8"; binmode STDERR, ":utf8"; my $c = ""; open C, ">:utf8", \$c; print C "š"; close C; die "Does not happen\n" if utf8::is_utf8($c); print utf8::decode($c) ? "Decoded\n" : "Undecoded\n";'; done | grep Undecoded | wc -l

作为引用并绝对肯定,我还制作了一个带有迂腐错误处理的版本——结果相同。
#!/usr/bin/perl
use warnings;
use strict;
use utf8;
binmode STDOUT, ":utf8" or die "Cannot binmode STDOUT\n";
binmode STDERR, ":utf8" or die "Cannot binmode STDERR\n";
my $c = "";
open C, ">:utf8", \$c or die "Cannot open: $!\n";
print C "š" or die "Cannot print: $!\n";
close C or die "Cannot close: $!\n";
die "Does not happen\n" if utf8::is_utf8($c);
print utf8::decode($c) ? "Decoded\n" : "Undecoded\n";

最佳答案

正在检查 $c详细显示与$c内容无关或其内部结构,以及 decode 的结果准确地表示它做了什么或没有做什么。

$ for i in {1..2}; do
     perl -MDevel::Peek -we'
        use strict; use utf8;
        binmode STDOUT, ":utf8";
        binmode STDERR, ":utf8";
        my $c = "";
        open C, ">:utf8", \$c;
        print C "š";
        close C;
        die "Does not happen\n" if utf8::is_utf8($c);
        Dump($c);
        print utf8::decode($c) ? "Decoded\n" : "Undecoded\n";
        Dump($c)
     '
     echo
  done


SV = PV(0x17c8470) at 0x17de990
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK)
  PV = 0x17d7a40 "\305\241"
  CUR = 2
  LEN = 16
Decoded
SV = PV(0x17c8470) at 0x17de990
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK,UTF8)
  PV = 0x17d7a40 "\305\241" [UTF8 "\x{161}"]
  CUR = 2
  LEN = 16


SV = PV(0x2d0fee0) at 0x2d26400
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK)
  PV = 0x2d1f4b0 "\305\241"
  CUR = 2
  LEN = 16
Undecoded
SV = PV(0x2d0fee0) at 0x2d26400
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK)
  PV = 0x2d1f4b0 "\305\241"
  CUR = 2
  LEN = 16

这是 utf8::decode 中的一个错误,但它已在 5.16.3 或更早版本(可能是 5.16.0)中修复,因为它仍然存在于 5.14.2 中。

一个合适的解决方法是使用 Encode 的 decode_utf8反而。

关于utf-8 - 在 Perl 中使用带有标量和 I/O 层的 open() 时编码的不确定性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25214937/

相关文章:

php - 修剪功能不起作用

list - 如何按给定顺序对列表进行排序?

perl - 如何在不使用 Perl 格式的情况下格式化列?

具有与 MySQL utf8_general_ci 归类相似特性的 Java Collat​​or

java - Web 服务中的 UTF-8 阿拉伯语编码

unicode - 在哪里可以找到有关字符编码的良好介绍?

C# Mysql UTF8编码

windows - 使用 MME 和 DirectMusic 时的 ANSI 或 OEM 代码页?

regex - 如何在 Perl 中替换字符串?

android - 在Android中解压unicode文件名错误