2010-12-02 4 views
7

У меня есть интересные результаты, пытающиеся разглядеть различия между использованием Encode::decode("utf8", $var) и utf8::decode($var). Я уже обнаружил, что вызов прежнего многократного значения переменной приведет к ошибке «Невозможно декодировать строку с широкими символами в ...», тогда как последний метод будет работать так много раз, как вам нужно, просто вернув false.Perl: utf8 :: decode vs. Encode :: decode

У меня возникли проблемы с пониманием того, как функция length возвращает разные результаты в зависимости от того, какой метод вы используете для декодирования. Проблема возникает из-за того, что я имею дело с «дважды закодированным» текстом utf8 из внешнего файла. Чтобы продемонстрировать эту проблему, я создал текстовый файл test.txt со следующими символами Unicode в одной строке: U + 00e8, U + 00ab, U + 0086, U + 000a. Эти символы Юникода представляют собой двойное кодирование символов 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:\[email protected]\n"; 
my @hex = (unpack('H*', $test)); 
print "Hex:\[email protected]\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:\[email protected]\n"; 
@hex = (unpack('H*', $test)); 
print "Hex:\[email protected]\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:\[email protected]\n"; 
@hex = (unpack('H*', $test)); 

print "Hex:\[email protected]\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 первый подсчитывает байты перед декодированием (как и ожидалось), а затем подсчитывает символы после первого декодирования, но затем подсчитывает байты снова после того, как второе декодирование (не ожидалось). Почему этот переключатель произойдет? Происходит ли в моем понимании, как работают эти функции декодирования?

Спасибо,
Matt

+1

Любая причина, по которой вы «требуете» модули вместо того, чтобы «использовать» их? – 2010-12-02 21:08:50

+1

Я не сделал use utf8, потому что это так говорит perl, что ваш код сам utf8-encoded, который мне не нужен (http://perldoc.perl.org/utf8.html). Полагаю, я мог бы use d Кодировать, но я просто не мог этого сделать. – Matt 2010-12-02 21:41:36

ответ

3

Вы не должны использовать функции из модуля utf8 прагмы. Its documentation так говорит:

Не используйте эту прагму для чего-либо еще, кроме как говорить Perl, что ваш скрипт написан в UTF-8.

Always use the Encode module, а также см. Вопрос Checklist for going the Unicode way with Perl. unpack слишком низкоуровневый, он даже не дает вам проверки ошибок.

Вы собираетесь неправильно с предположением о том, что octects 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 "\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"]