Mat*_*att 7 perl encoding utf-8 decoding
我有一些有趣的结果试图辨别使用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";
Run Code Online (Sandbox Code Playgroud)
这给出了以下输出:
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时(替换所有$ 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
似乎perl首先计算解码前的字节数(如预期的那样),然后在第一次解码后对字符进行计数,但在第二次解码后再次对字节进行计数(不是预期的).为什么会发生这种转变?我对这些解码功能如何工作有所了解吗?
谢谢,
马特
您不应该使用utf8pragma 模块中的函数。它的文档是这样说的:
\n\n\n除了告诉 Perl 您的脚本是用 UTF-8 编写的之外,请勿将此编译指示用于其他任何用途。
\n
始终使用 Encode 模块,另请参阅问题Checklist for going the Unicode way with Perl。unpack太低级了,它甚至不给你错误检查。
您假设八位字节是 UTF-8 对字符和进行双重编码E8 AB 86 0A的结果,这是错误的。这是这些字符的单个 UTF-8 编码的表示形式。也许你的整个困惑都源于这个错误。\xe8\xab\x86newline
length不适当地重载,在某些时候它确定字符长度或八位字节长度。使用更好的工具,例如Devel::Peek.
#!/usr/bin/env perl\nuse strict;\nuse warnings FATAL => \'all\';\nuse Devel::Peek qw(Dump);\nuse Encode qw(decode);\n\nmy $test = "\\x{00e8}\\x{00ab}\\x{0086}\\x{000a}";\n# or read the octets without implicit decoding from a file, does not matter\n\nDump $test;\n# FLAGS = (PADMY,POK,pPOK)\n# PV = 0x8d8520 "\\350\\253\\206\\n"\\0\n\n$test = decode(\'UTF-8\', $test, Encode::FB_CROAK);\nDump $test;\n# FLAGS = (PADMY,POK,pPOK,UTF8)\n# PV = 0xc02850 "\\350\\253\\206\\n"\\0 [UTF8 "\\x{8ac6}\\n"]\nRun Code Online (Sandbox Code Playgroud)\n