8

我有一些有趣的结果,试图辨别使用Encode::decode("utf8", $var)和之间的区别utf8::decode($var)。我已经发现,在一个变量上多次调用前者最终会导致错误“无法在...处解码带有宽字符的字符串”,而后一种方法会很高兴地运行任意多次,只是返回 false。

我无法理解的是length函数如何根据您用于解码的方法返回不同的结果。出现问题是因为我正在处理来自外部文件的“双重编码”utf8 文本。为了演示这个问题,我创建了一个文本文件“test.txt”,其中一行包含以下 Unicode 字符:U+00e8、U+00ab、U+0086、U+000a。这些 Unicode 字符是 Unicode 字符 U+8acb 和换行符的双重编码。该文件以 UTF8 编码到磁盘。然后我运行以下 perl 脚本:

#!/usr/bin/perl                                                                                                                                          
use strict;
use warnings;
require "Encode.pm";
require "utf8.pm";

open FILE, "test.txt" or die $!;
my @lines = <FILE>;
my $test =  $lines[0];

print "Length: " . (length $test) . "\n";
print "utf8 flag: " . utf8::is_utf8($test) . "\n";
my @unicode = (unpack('U*', $test));
print "Unicode:\n@unicode\n";
my @hex = (unpack('H*', $test));
print "Hex:\n@hex\n";

print "==============\n";

$test = Encode::decode("utf8", $test);
print "Length: " . (length $test) . "\n";
print "utf8 flag: " . utf8::is_utf8($test) . "\n";
@unicode = (unpack('U*', $test));
print "Unicode:\n@unicode\n";
@hex = (unpack('H*', $test));
print "Hex:\n@hex\n";

print "==============\n";

$test = Encode::decode("utf8", $test);
print "Length: " . (length $test) . "\n";
print "utf8 flag: " . utf8::is_utf8($test) . "\n";
@unicode = (unpack('U*', $test));
print "Unicode:\n@unicode\n";
@hex = (unpack('H*', $test));

print "Hex:\n@hex\n";

这给出了以下输出:

长度:7
utf8 标志:
统一码:
195 168 194 171 194 139 10
十六进制:
c3a8c2abc28b0a
===============
长度:4
UTF8 标志:1
统一码:
232 171 139 10
十六进制:
c3a8c2abc28b0a
===============
长度:2
UTF8 标志:1
统一码:
35531 10
十六进制:
e8ab8b0a

这是我所期望的。长度原来是 7,因为 perl 认为 $test 只是一系列字节。解码一次后,perl 知道 $test 是一系列 utf8 编码的字符(即 perl 不是返回 7 个字节的长度,而是返回 4 个字符的长度,即使 $test 在内存中仍然是 7 个字节)。在第二次解码之后,$test 包含解释为 2 个字符的 4 个字节,这是我所期望的,因为 Encode::decode 采用了 4 个代码点并将它们解释为 utf8 编码的字节,从而产生 2 个字符。奇怪的是,当我修改代码以调用 utf8::decode 时(将所有 $test = Encode::decode("utf8", $test); 替换为 utf8::decode($test))

这给出了几乎相同的输出,只是长度的结果不同:

长度:7
utf8 标志:
统一码:
195 168 194 171 194 139 10
十六进制:
c3a8c2abc28b0a
===============
长度:4
UTF8 标志:1
统一码:
232 171 139 10
十六进制:
c3a8c2abc28b0a
===============
长度:4
UTF8 标志:1
统一码:
35531 10
十六进制:
e8ab8b0a

似乎 perl 在解码前首先计算字节数(如预期的那样),然后在第一次解码后计算字符数,然后在第二次解码后再次计算字节数(不是预期的)。为什么会发生这种转变?我对这些解码功能如何工作的理解有偏差吗?

谢谢,
马特

4

2 回答 2

4
于 2010-12-03T14:04:04.570 回答
2

原来这是一个错误:https ://rt.perl.org/rt3//Public/Bug/Display.html?id=80190 。

于 2011-10-21T18:45:00.860 回答