Ответ 1
Да!
Очевидно, лучше исправить программу, создающую файл, но это не всегда возможно. Ниже следует два решения.
Строка может содержать сочетание кодировок
Encoding::FixLatin предоставляет функцию с именем fix_latin
, которая декодирует текст, который состоит из сочетания UTF-8, iso-8859-1, cp1252 и US-ASCII.
$ perl -e'
use Encoding::FixLatin qw( fix_latin );
$bytes = "\xD0 \x92 \xD0\x92\n";
$text = fix_latin($bytes);
printf("U+%v04X\n", $text);
'
U+00D0.0020.2019.0020.0412.000A
Используются эвристики, но они достаточно надежны. Только следующие случаи не будут выполнены:
-
One of
[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß]
encoded using iso-8859-1 or cp1252, followed by one of
[€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ<NBSP>
¡¢£¤¥¦§¨©ª«¬<SHY>
®¯°±²³´µ¶·¸¹º»¼½¾¿]
encoded using iso-8859-1 or cp1252. -
One of
[àáâãäåæçèéêëìíîï]
encoded using iso-8859-1 or cp1252, followed by two of
[€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ<NBSP>
¡¢£¤¥¦§¨©ª«¬<SHY>
®¯°±²³´µ¶·¸¹º»¼½¾¿]
encoded using iso-8859-1 or cp1252. -
One of
[ðñòóôõö÷]
encoded using iso-8859-1 or cp1252, followed by two of
[€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ<NBSP>
¡¢£¤¥¦§¨©ª«¬<SHY>
®¯°±²³´µ¶·¸¹º»¼½¾¿]
encoded using iso-8859-1 or cp1252.
Тот же результат может быть получен с использованием основного модуля Encode, хотя я предполагаю, что это справедливый бит медленнее, чем Encoding:: FixLatin с Encoding:: FixLatin:: XS установлен.
$ perl -e'
use Encode qw( decode_utf8 encode_utf8 decode );
$bytes = "\xD0 \x92 \xD0\x92\n";
$text = decode_utf8($bytes, sub { encode_utf8(decode("cp1252", chr($_[0]))) });
printf("U+%v04X\n", $text);
'
U+00D0.0020.2019.0020.0412.000A
В каждой строке используется только одна кодировка
fix_latin
работает на уровне персонажа. Если известно, что каждая строка полностью закодирована с использованием одного из UTF-8, iso-8859-1, cp1252 или US-ASCII, вы можете сделать процесс еще более надежным, если проверить правильность линии UTF-8.
$ perl -e'
use Encode qw( decode );
for $bytes ("\xD0 \x92 \xD0\x92\n", "\xD0\x92\n") {
if (!eval {
$text = decode("UTF-8", $bytes, Encode::FB_CROAK|Encode::LEAVE_SRC);
1 # No exception
}) {
$text = decode("cp1252", $bytes);
}
printf("U+%v04X\n", $text);
}
'
U+00D0.0020.2019.0020.00D0.2019.000A
U+0412.000A
Используются эвристики, но они очень надежны. Они будут только терпеть неудачу, если для данной строки истинны все:
-
Строка кодируется с использованием iso-8859-1 или cp1252,
-
At least one of
[€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ<NBSP>
¡¢£¤¥¦§¨©ª«¬<SHY>
®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷]
is present in the line, -
All instances of
[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß]
are always followed by exactly one of
[€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ<NBSP>
¡¢£¤¥¦§¨©ª«¬<SHY>
®¯°±²³´µ¶·¸¹º»¼½¾¿], -
All instances of
[àáâãäåæçèéêëìíîï]
are always followed by exactly two of
[€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ<NBSP>
¡¢£¤¥¦§¨©ª«¬<SHY>
®¯°±²³´µ¶·¸¹º»¼½¾¿], -
All instances of
[ðñòóôõö÷]
are always followed by exactly three of
[€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ<NBSP>
¡¢£¤¥¦§¨©ª«¬<SHY>
®¯°±²³´µ¶·¸¹º»¼½¾¿], -
Ничего из присутствующих [& # xF8; & # xF9; & # xFA; & # xFB; & # xFC; & # xFD; & # xFE; & # xFF;]
в строке и -
None of
[€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ<NBSP>
¡¢£¤¥¦§¨©ª«¬<SHY>
®¯°±²³´µ¶·¸¹º»¼½¾¿]
are present in the line except where previously mentioned.
Примечания:
- Encoding:: FixLatin устанавливает инструмент командной строки
fix_latin
для преобразования файлов, и было бы тривиально писать один, используя второй подход. -
fix_latin
(как функция, так и файл) можно ускорить, установив Encoding::FixLatin::XS. - Такой же подход можно использовать для микширования UTF-8 с другими однобайтовыми кодировками. Надежность должна быть одинаковой, но она может меняться.