我无法理解的是长度函数如何根据您用于解码的方法返回不同的结果.问题出现是因为我正在处理来自外部文件的“双重编码”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";
这给出了以下输出:
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首先计算解码前的字节数(如预期的那样),然后在第一次解码后对字符进行计数,然后在第二次解码后再次对字节进行计数(不是预期的).为什么会发生这种转变?我对这些解码功能如何工作有所了解吗?
谢谢,马特
解决方法
Do not use this pragma for anything else than telling Perl that your script is written in UTF-8.
Always use the Encode module,还看到问题Checklist for going the Unicode way with Perl.解压缩程度太低,它甚至不会给你错误检查.
假设octect E8 AB 86 0A是UTF-8对字符諆和换行符进行双重编码的结果,你会出错.这是这些字符的单个UTF-8编码的表示.也许你身边的整个混乱源于这个错误.
长度不正确地重载,在某些时候它确定字符的长度,或八位字节的长度.使用更好的工具,如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,pPOK,UTF8) # PV = 0xc02850 "\350\253\206\n"\0 [UTF8 "\x{8ac6}\n"]