Преобразование строки UTF8 в ASCII в Perl

Я пробовал все, что рекомендовали Google и StackOverflow (что я мог найти), включая использование Encode. Мой код работает, но он просто использует UTF8, и я получаю предупреждения широкого характера. Я знаю, как обойти эти предупреждения, но я не использую UTF8 для чего-либо еще, поэтому я хотел бы просто преобразовать его и не нужно адаптировать остальную часть моего кода, чтобы справиться с этим. Здесь мой код:

my $xml = XMLin($content);
# Populate the @titles array with each item title.
my @titles;
for my $item (@{$xml->{channel}->{item}}) {
    my $title = Encode::decode_utf8($item->{title});
    #my $title = $item->{title};
    #utf8::downgrade($title, 1);
    Encode::from_to($title, 'utf8', 'iso-8859-1');
    push @titles, $title;
}
return @titles;

Прокомментировал, что вы можете увидеть некоторые другие вещи, которые я пробовал. Мне хорошо известно, что я не знаю, что я здесь делаю. Я просто хочу, чтобы в итоге получилась простая старая строка ASCII. Любые идеи очень приветствуются. Спасибо.

Ответ 1

Ответ зависит от того, как вы хотите использовать заголовок. Существует 3 основных способа:

  • Байты, которые представляют кодированную строку UTF-8.

Это формат, который следует использовать, если вы хотите хранить кодированную строку UTF-8 вне вашего приложения, будь то на диске или по электронной почте, или за пределами вашей программы.

  • Строка символов Юникода.

Понятие символов является внутренним для Perl. Когда вы выполняете Encode::decode_utf8, тогда куча байтов пытается преобразовать в строку символов, как видно из Perl. Perl VM (и программист, пишущий Perl-код) не может экстернализировать эту концепцию, кроме как через декодирование байтов UTF-8 на входе и кодирование их на байты UTF-8 на выходе. Например, ваша программа получает два байта в качестве ввода, который, как вы знаете, представляет кодированный символ (символы) UTF-8, скажем 0xC3 0xB6. В этом случае decode_utf8 возвращает представление, которое вместо двух байтов видит один символ: ö.

Затем вы можете перейти к обработке этой строки в Perl. Чтобы проиллюстрировать разницу, рассмотрим следующий код:

my $bytes = "\xC3\xB6";
say length($bytes); # prints "2"
my $string = decode_utf8($bytes);
say length($string); # prints "1"
  • Частный случай ASCII, подмножество UTF-8.

    ASCII - очень небольшое подмножество Unicode, где символы в этом диапазоне представлены одним байтом. Преобразование Unicode в ASCII является операцией с потерями, поскольку большинство символов Unicode не являются символами ASCII. Вы либо вынуждены бросать каждый символ в своей строке, который не находится в ASCII, либо пытаться сопоставить символ Юникода с их ближайшими эквивалентами ASCII (что невозможно в подавляющем большинстве случаев) при попытке принудить Unicode строка в ASCII.

Поскольку у вас широкоформатные предупреждения, это означает, что вы пытаетесь манипулировать (возможно, выводить) символы Unicode, которые не могут быть представлены как ASCII или ISO-8859-1.

Если вам не нужно манипулировать заголовком из XML-документа в виде строки, я бы предложил оставить его как байты UTF-8 (я бы сказал, что вы должны быть осторожны, чтобы не смешивать байты и символы в строках). Если вам нужно манипулировать им, затем декодировать, манипулировать и на выходе кодировать его в UTF-8.

Для дальнейшего чтения используйте perldoc для изучения perlunitut, perlunifaq, perlunicode, perluniintro и Encode.

Ответ 2

Хотя это старый вопрос, я просто провел несколько часов (!), пытаясь сделать более или менее то же самое! То есть: чтение данных из XML файла UTF-8 и преобразование этих данных в кодовую страницу Windows-1252 (я мог бы также использовать Latin1, ISO-8859-1 и т.д.), Чтобы иметь возможность создавать имена файлов с акцентированными буквами.

После долгих экспериментов и еще большего поиска мне наконец удалось заставить преобразование работать. "Трюк" заключается в использовании Encode:: encode вместо Encode:: decode.

Например, учитывая код в исходном вопросе, правильный (или хотя бы один способ:-) для преобразования из UTF-8 будет выглядеть следующим образом:

my $title = Encode::encode("Windows-1252", $item->{title});

или

my $title = Encode::encode("ISO-8859-1", $item->{title});

или

my $title = Encode::encode("<your-favourite-codepage-here>", $item->{title});

Я надеюсь, что это поможет другим, имеющим схожие проблемы!

Ответ 3

Вы можете использовать следующую строку, чтобы просто избавиться от предупреждения. Это предполагает, что вы хотите использовать UTF8, что обычно не должно быть проблемой.

binmode(STDOUT, ":encoding(utf8)");