Perl:utf8::decode与Encode::decode

Perl:utf8::decode与Encode::decode,perl,encoding,utf-8,decoding,Perl,Encoding,Utf 8,Decoding,我得到了一些有趣的结果,试图区分使用Encode::decode(“utf8”,$var)和utf8::decode($var)之间的区别。我已经发现,对一个变量多次调用前一个方法最终会导致错误“无法解码在…处具有宽字符的字符串”,而后一个方法将愉快地运行任意次数,只需返回false 我很难理解的是length函数如何根据解码方法返回不同的结果。问题的出现是因为我正在处理来自外部文件的“双重编码”utf8文本。为了演示这个问题,我创建了一个文本文件“test.txt”,其中一行包含以下Unico

我得到了一些有趣的结果,试图区分使用
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";
#/usr/bin/perl
严格使用;
使用警告;
需要“Encode.pm”;
需要“utf8.pm”;
打开文件“test.txt”或die$!;
我的@lines=;
my$test=$lines[0];
打印“长度:”。(长度$test)。“\n”;
打印“utf8标志:”。utf8::is_utf8($test)。“\n”;
my@unicode=(解包('U*',$test));
打印“Unicode:\n@unicode\n”;
my@hex=(解包('H*',$test));
打印“十六进制:\n@hex\n”;
打印“======================\n”;
$test=Encode::decode(“utf8”,$test);
打印“长度:”。(长度$test)。“\n”;
打印“utf8标志:”。utf8::is_utf8($test)。“\n”;
@unicode=(解包('U*',$test));
打印“Unicode:\n@unicode\n”;
@十六进制=(解包('H*',$test));
打印“十六进制:\n@hex\n”;
打印“======================\n”;
$test=Encode::decode(“utf8”,$test);
打印“长度:”。(长度$test)。“\n”;
打印“utf8标志:”。utf8::is_utf8($test)。“\n”;
@unicode=(解包('U*',$test));
打印“Unicode:\n@unicode\n”;
@十六进制=(解包('H*',$test));
打印“十六进制:\n@hex\n”;
这将提供以下输出:

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 utf8标志: Unicode: 195 168 194 171 194 139 10 十六进制: c3a8c2abc28b0a ============== 长度:4 utf8标志:1 Unicode: 232 171 139 10 十六进制: c3a8c2abc28b0a ============== 长度:2 utf8标志:1 Unicode: 35531 10 十六进制: e8ab8b0a 这就是我所期望的。长度最初是7,因为perl认为$test只是一系列字节。在解码一次之后,perl知道$test是utf8编码的一系列字符(即,perl返回的长度不是7个字节,而是4个字符,即使$test在内存中仍然是7个字节)。在第二次解码之后,$test包含4个字节,解释为2个字符,这是我所期望的,因为Encode::decode获取了4个代码点,并将它们解释为utf8编码的字节,结果是2个字符。奇怪的是,当我修改代码以调用utf8::decode时(将所有$test=Encode::decode(“utf8”,$test);替换为utf8::decode($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 长度:7 utf8标志: Unicode: 195 168 194 171 194 139 10 十六进制: c3a8c2abc28b0a ============== 长度:4 utf8标志:1 Unicode: 232 171 139 10 十六进制: c3a8c2abc28b0a ============== 长度:4 utf8标志:1 Unicode: 35531 10 十六进制: e8ab8b0a 似乎perl在解码前首先计算字节数(如预期的那样),然后在第一次解码后计算字符数,但在第二次解码后再次计算字节数(如预期的那样)。为什么会发生这种转变?我对这些解码功能是如何工作的理解有误吗


谢谢,
Matt

您不应该使用
utf8
pragma模块中的功能。他这样说:

除了告诉Perl您的脚本是用UTF-8编写的之外,不要使用这个pragma

,并看到问题<代码>解包的级别太低,甚至不提供错误检查

假设八位字节
E8 AB 86 0A
是UTF-8双重编码的结果,这是错误的諆
换行符
。这是这些字符的单个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 "\350\253\206\n"\0

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

原来这是一个bug:。

你为什么
需要
而不是
使用
模块呢?我没有
使用
utf8,因为这样做告诉perl你的代码本身是utf8编码的,我不需要()。我想我本可以
使用
d编码,但我碰巧没有。谢谢你的回复。perl文档确实说可以使用utf8模块中的函数。引用后面的句子是“下面描述的实用程序函数在不使用utf8;的情况下可以直接使用”,也就是说,如果不需要,不应该“使用”(perl关键字use)utf8 pragma,但可以使用(英语使用)其函数。此外,我意识到“eaab860a”是单一编码。我的文件包含八位字节“c3a8c2abc28b0a”,这是双重编码。事实证明,我的困惑源于“length”函数中的一个bug。请看,它实际上说“除了告诉Perl您的脚本是用UTF-8编写的之外,不要使用这个pragma。下面描述的实用程序函数不使用utf8;就可以直接使用”,这显然并不意味着“您不应该使用utf8 pragma模块中的函数”。这意味着您不需要使用pragma来导入函数。