LWP :: Simple :: получить изменения кодировки

Полагаю, я неправильно использую LWP :: Simple :: get, но я не понимаю, как это исправить. Моя первая попытка была простой

perl -e 'use LWP::Simple; print get("http://localhost/wtf.txt");'

, но это не сработало. wtf.txt содержит один символ в кодировке UTF-8 u+00f6 (т. Е. ö). Используя wget и xxd, я убедился, что HTTP-сервер отправляет правильную строку заголовка Content-Type: text/plain; charset=utf-8 и что содержимое соответствует ожиданиям. Но приведенный выше код perl вместо этого возвращает u+00f6 в кодировке ISO-8859-1.

Я думал, что это простая проблема с кодировкой с простым исправлением, но копнув глубже, я обнаружил, что это не так просто, как я надеялся. Я создал второй файл wtf2.txt с одним символом u+30e4 в кодировке UTF-8 (т.е. ) и получил оба с помощью следующего кода Perl:

#!/usr/bin/perl
use LWP::Simple;
$wtf=get("http://localhost/$ARGV[0]");
$wtf2=pack("H*",unpack("H*",$wtf));
print $wtf;
print "\n";
print $wtf2;
print "\n$wtf\n$wtf2\n";
print (unpack("H*",$wtf)."\n");

При выборке wtf.txt этот код записывает 4 раза u+00f6 в своей форме, закодированной в ISO-8859-1, за которой следует f6 (его форма в кодировке ISO-8859-1 в шестнадцатеричной форме). До сих пор все как раньше. Но при получении wtf2.txt этот код записывает u+30e4 в кодировке UTF-8, за которой следует u+00e4 (т.е. ä) в ISO-8859-1, u+30e4 в UTF-8, u+00e4 в UTF-8, e4 (ISO-8859- 1 из u+00e4 в шестнадцатеричном формате).

Учитывая, что u+30e4 и u+00e4 не имеют ничего общего друг с другом, кроме того, что последний является побитовой маской / усеченной версией первого, я ожидаю, что внутри LWP :: Simple происходит не только перекодирование, но и некоторое усечение. Я склонен отправить отчет об ошибке в LWP :: Simple, но я все еще надеюсь на простое исправление и / или объяснение.

Кстати, ни одна из описанных проблем не возникает, если я заменяю вторую и третью строку на $wtf=<>; и просто читаю файлы из stdin вместо того, чтобы получать их через LWP :: Simple :: get.

Я тестировал это с помощью perl 5.14.2 и libwww 6.04 на Debian 7.


person user2845840    schedule 05.11.2016    source источник
comment
Вы также можете увидеть http://stackoverflow.com/q/2341128/2766176.   -  person brian d foy    schedule 06.11.2016


Ответы (1)


Это ошибка вашего кода.

LWP::Simple::get не возвращает исходные байты (в некоторой кодировке), он возвращает декодированный текст (например, Unicode). (Что имеет смысл, потому что, если бы он вернул байты, вы бы не знали, как их декодировать, потому что get не сообщает вам кодировку.)

Итак, get("http://localhost/wtf.txt") возвращает строку, содержащую кодовую точку U + 00f6. print затем записывает несколько байтов в STDOUT. Что это за байты? Это зависит от уровня кодирования, установленного в данный момент для дескриптора файла. По умолчанию это странная смесь Latin-1 и UTF-8 (это может даже зависеть от внутренней кодировки строки).

Если вы хотите получить вывод UTF-8, сначала выполните binmode STDOUT, ":encoding(UTF-8)";. Это гарантирует, что весь текст, записанный в STDOUT, будет закодирован как UTF-8.

С другой стороны, если вы хотите игнорировать кодировки и просто записывать байты, полученные от веб-сервера, то LWP::Simple - неправильный выбор. Вместо этого используйте LWP::UserAgent и позвоните $response->content. (LWP::Simple::get использует $response->decoded_content внутри.)

Усечение во втором примере, вероятно, связано с _11 _ / _ 12_, что не имеет смысла в строках Unicode (они предназначены для байтовых строк, то есть всех кодовых точек ‹= 255).

person melpomene    schedule 05.11.2016
comment
Спасибо. Оба binmode STDOUT и LWP::UserAgent работают. pack / unpack были рекомендуемым способом просмотра шестнадцатеричной версии данных Perl. Есть ли лучшая альтернатива, которая дает мне неизменное шестнадцатеричное / восьмеричное / десятичное представление о том, что Perl хранит в своих переменных? Если бы у меня было это, я бы сам мог отладить это и не беспокоился бы о stackoverflow с ним. - person user2845840; 08.11.2016
comment
@ user2845840 Если вы хотите узнать, что, по мнению perl, содержит в своих строках, используйте printf "%vd\n", $str (десятичный) или printf "%vx\n", $str (шестнадцатеричный). Вывод будет в десятичной (или шестнадцатеричной) форме с точками, с одним числом для каждой кодовой точки (это также говорит вам, что perl считает длиной строки (количество точек + 1)). - person melpomene; 09.11.2016