Perl:utf8 :: decode与Encode :: decode

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

我很难理解的是,根据您使用哪种方法解码, length函数如何返回不同的结果。 问题出现是因为我正在处理来自外部文件的“双重编码”utf8文本。 为了演示这个问题,我在一行中创建了一个带有以下Unicode字符的文本文件“test.txt”: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@unicoden";
my @hex = (unpack('H*', $test));
print "Hex:n@hexn";

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@unicoden";
@hex = (unpack('H*', $test));
print "Hex:n@hexn";

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@unicoden";
@hex = (unpack('H*', $test));

print "Hex:n@hexn";

这给出了以下输出:

Length: 7
utf8 flag: 
Unicode:
195 168 194 171 194 139 10
Hex:
c3a8c2abc28b0a
==============
Length: 4
utf8 flag: 1
Unicode:
232 171 139 10
Hex:
c3a8c2abc28b0a
==============
Length: 2
utf8 flag: 1
Unicode:
35531 10
Hex:
e8ab8b0a

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

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

Length: 7
utf8 flag: 
Unicode:
195 168 194 171 194 139 10
Hex:
c3a8c2abc28b0a
==============
Length: 4
utf8 flag: 1
Unicode:
232 171 139 10
Hex:
c3a8c2abc28b0a
==============
Length: 4
utf8 flag: 1
Unicode:
35531 10
Hex:
e8ab8b0a

看起来perl首先对解码前的字节进行计数(如预期的那样),然后在第一次解码后对字符进行计数,但在第二次解码后再次对字节进行计数(不是预期的)。 为什么会发生这种转换? 我对理解这些解码函数的工作方式是否存在失误?

谢谢,
马特


你不应该使用utf8 pragma模块中的函数。 其文档如此说明:

除了告诉Perl您的脚本使用UTF-8编写以外,不要使用此编译指示。

总是使用Encode模块,并且看到使用Perl进行Unicode方式的问题核对表。 unpack太低级,它甚至不会给你错误检查。

假设字符E8 AB 86 0A是UTF-8对字符newline 双重编码的结果,您就错了。 这是这些字符的单个UTF-8编码的表示。 也许整个你身边的困惑都源于这个错误。

length不适当地过载,在某些时候它确定字符的长度,或者以八位字节为单位的长度。 使用更好的工具,如Devel::Peek

#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use Devel::Peek qw(Dump);
use Encode qw(decode);

my $test = "x{00e8}x{00ab}x{0086}x{000a}";
# or read the octets without implicit decoding from a file, does not matter

Dump $test;
#  FLAGS = (PADMY,POK,pPOK)
#  PV = 0x8d8520 "350253206n"

$test = decode('UTF-8', $test, Encode::FB_CROAK);
Dump $test;
#  FLAGS = (PADMY,POK,pPOK,UTF8)
#  PV = 0xc02850 "350253206n" [UTF8 "x{8ac6}n"]

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

链接地址: http://www.djcxy.com/p/66359.html

上一篇: Perl: utf8::decode vs. Encode::decode

下一篇: 8: General? Bin? Unicode?