Принудительная смешанная многострочная строка ISO-8859-1 и UTF-8 в UTF-8 в Perl - PullRequest
3 голосов
/ 31 марта 2010

Рассмотрим следующую проблему:

Многострочная строка $junk содержит несколько строк, которые кодируются в UTF-8, а некоторые - в ISO-8859-1. Я не знаю априори , какие строки в какой кодировке, поэтому потребуется эвристика.

Я хочу превратить $junk в чистый UTF-8 с надлежащим перекодированием строк ISO-8859-1. Кроме того, в случае ошибок при обработке я хочу предоставить «лучший результат», а не выдавать ошибку.

Моя текущая попытка выглядит так:

$junk = force_utf8($junk);

sub force_utf8 {
  my $input = shift;
  my $output = '';
  foreach my $line (split(/\n/, $input)) {
    if (utf8::valid($line)) {
      utf8::decode($line);
    }
    $output .= "$line\n";
  }
  return $output;
}

Очевидно, что преобразование никогда не будет идеальным, поскольку нам не хватает информации об исходном кодировании каждой строки. Но разве это «лучший результат», который мы можем получить?

Как бы вы улучшили эвристику / функциональность сабвуфера force_utf8(...)?

Ответы [ 5 ]

2 голосов
/ 01 апреля 2010

У меня нет никаких полезных советов, кроме того, что я бы попробовал сначала использовать Encode :: Guess .

2 голосов
/ 31 марта 2010

Возможно, вам удастся исправить это, используя немного знаний предметной области. Например, © не является вероятной комбинацией символов в ISO-8859-1; гораздо вероятнее будет UTF-8 é.

Если ваш ввод ограничен ограниченным пулом символов, вы также можете использовать эвристику, например, предполагая, что М никогда не возникнет в вашем потоке ввода.

Без такого знания предметной области ваша проблема в целом неразрешима.

1 голос
/ 31 марта 2010

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

1 голос
/ 31 марта 2010

Просто взглянув на символ, будет трудно определить, кодируется ли он в кодировке ISO-8859-1 или UTF-8. Проблема в том, что оба являются 8-битными кодировками, поэтому просто посмотреть на MSb недостаточно. Тогда для каждой строки я бы перекодировал строку, предполагая, что это UTF-8. При обнаружении недопустимой кодировки UTF-8 перекодируйте строку, предполагая, что строка действительно соответствует ISO-8859-1. Проблема этой эвристики в том, что вы можете перекодировать строки ISO-8859-1, которые также являются правильно сформированными линиями UTF-8; однако без внешней информации о $junk невозможно определить, что уместно.

0 голосов
/ 18 декабря 2010

Короче говоря, я решил решить мою проблему с помощью "file -bi" и "iconv -f ISO-8859-1 -t UTF-8".

Недавно я столкнулся с подобной проблемой, пытаясь нормализовать кодировку имен файлов. У меня была смесь ISO-8859-1, UTF-8 и ASCII. Поняв, что при обработке файлов я добавил сложности, вызванные тем, что имя каталога имеет одну кодировку, которая отличается от кодировки файла.

Первоначально я пытался использовать Perl, но он не мог правильно различить UTF-8 и ISO-8859-1, что привело к искажению UTF-8.

В моем случае это было однократное преобразование при разумном количестве файлов, поэтому я выбрал медленный метод, о котором я знал, и который работал без ошибок (в основном потому, что использовалось только 1-2 несмежных символа в строке). специальные коды ISO-8859-1)

Опция # 1 преобразует ISO-8859-1 в UTF-8

cat mixed_text.txt |
while read i do
type=${"$(echo "$i" | file -bi -)"#*=}
if [[ $type == 'iso-8859-1' ]]; then
    echo "$i" | iconv -f ISO-8859-1 -t UTF-8
else
    echo "$i"
fi
done > utf8_text.txt

Опция №2 преобразуется в ISO-8859-1 в ASCII

cat mixed_text.txt |
while read i do
type=${"$(echo "$i" | file -bi -)"#*=}
if [[ $type == 'iso-8859-1' ]]; then
    echo "$i" | iconv -f ISO-8859-1 -t ASCII//TRANSLIT
else
    echo "$i"
fi
done > utf8_text.txt
...