Правильная передача UTF-8 в Perl

Мне дали файл, (вероятно) закодированный в Latin-1 (ISO 8859-1), и с ним нужно выполнить некоторые преобразования и интеллектуальный анализ данных. Предполагается, что вывод должен быть в UTF-8, и я пробовал все, что мог найти о преобразовании кодировки в Perl, ни один из них не дал никакого полезного вывода.

Я знаю, что use utf8; с самого начала ничего не делает. Я попробовал пакет Encode, который выглядел многообещающе:

open FILE, '<', $ARGV[0] or die $!;

my %tmp = ();
my $last_num = 0;

while (<FILE>) {
    $_ = decode('ISO-8859-1', encode('UTF-8', $_));

    chomp;
    next unless length;
    process($_);
}

Я пробовал это в любой комбинации, которую мог придумать, а также добавлял binmode(STDOUT, ":utf8");, open FILE, '<:encoding(ISO-8859-1)', $ARGV[0] or die $!; и многое другое. Результатом были либо зашифрованные умляуты, либо сообщение об ошибке типа \xC3 is not a valid UTF-8 character, либо даже смешанный текст (некоторые в UTF-8, некоторые в Latin-1).

Все, что мне нужно, это простой способ чтения текстового файла Latin-1 и создания вывода UTF-8 на консоли через print. Есть ли простой способ сделать это в Perl?


person Lanbo    schedule 03.08.2012    source источник
comment
Perl не знаю, как нормально работать с utf :(   -  person gaussblurinc    schedule 03.08.2012


Ответы (3)


См. Введение в кодировку Perl и поваренная книга Unicode.

  • Проще всего с piconv:

    $ piconv -f Latin1 -t UTF-8 < input.file > output.file
    
  • Легко, со слоями кодирования:

    use autodie qw(:all);
    open my $input, '<:encoding(Latin1)', $ARGV[0];
    binmode STDOUT, ':encoding(UTF-8)';
    
  • Умеренно, с ручным де-/кодированием:

    use Encode qw(decode encode);
    use autodie qw(:all);
    
    open my $input, '<:raw', $ARGV[0];
    binmode STDOUT, ':raw';
    while (my $raw = <$input>) {
        my $line = decode 'Latin1', $raw, Encode::FB_CROAK | Encode::LEAVE_SRC;
        my $result = process($line);
        print {STDOUT} encode 'UTF-8', $result, Encode::FB_CROAK | Encode::LEAVE_SRC;
    }
    
person daxim    schedule 03.08.2012
comment
Единственная проблема, с которой вы столкнетесь с подходом daxim, заключается в том, что файл на самом деле не в Latin1 - файлы в сочетании кодировок - это кошмар, с которым нужно иметь дело, независимо от того, что вы делаете, к сожалению. - person Richard Huxton; 03.08.2012
comment
@RichardHuxton Есть ли шанс справиться с этим? Я подозреваю, что некоторые данные, которые мне дали, имеют смешанные кодировки. - person Lanbo; 03.08.2012
comment
Есть Encode::Guess, но я боюсь, что почти невозможно отличить многие из 8-битных наборов символов, не зная заранее, что это за содержимое. Например, 8859-15 имеет символ евро, поэтому финансовая информация с большим количеством кодовых точек 0xA4, скорее всего, соответствует этому, а не 8859-1. Точно так же некоторые символы с валлийским акцентом находятся в 8859-14. Однако, не зная, что означает текст, это очень тяжелая работа. И это без учета умных кавычек Microsoft-Word, возникающих там, где люди вырезали и вставляли из Word. - person Richard Huxton; 03.08.2012
comment
Если вы собираетесь декодировать что-то самостоятельно, вам лучше убедиться, что вы читаете необработанный поток байтов. В этом случае вы оставили декодирование по умолчанию тому, что решит сделать чтение файла, на что можно повлиять издалека. То же самое касается вывода. Вы должны быть уверены, что на STDOUT не настроено что-то для кодирования того, что вы ему даете. - person brian d foy; 03.08.2012

Может как:

$_ = encode('utf-8', decode('ISO-8859-1', $_));

Данные кодируются gb2312, поэтому их можно преобразовать в utf-8:

#!/usr/bin/env perl

use Encode qw(encode decode);

while (<DATA>) {
    $_ = encode('utf-8', decode('gb2312', $_));
    print;
}

__DATA__
Â׶ذÂÔË»á
person cdtits    schedule 03.08.2012

$_ = decode('ISO-8859-1', encode('UTF-8', $_));

У этой линии есть две проблемы. Сначала вы кодируете свой ввод в UTF-8, а затем декодируете его из ISO-8859-1. Эти две операции неверны.

Во-вторых, вы почти наверняка не хотите декодировать и кодировать одновременно. Золотое правило обработки кодировок символов в Perl заключается в следующем:

  1. Декодируйте данные, как только вы получите их из внешнего мира. Это берет ваш входной поток байтов и преобразует его во внутреннее представление Perl для символьных строк.
  2. Обработайте данные в соответствии с вашими требованиями.
  3. Кодируйте данные непосредственно перед их отправкой во внешний мир. Это берет внутреннее представление Perl для строк символов и преобразует его в правильно закодированный поток байтов для требуемой выходной кодировки.
person Dave Cross    schedule 03.08.2012