Как проверить в Perl, если файл записан с прямым или прямым порядком байтов?
На самом деле мне нужно разобрать некоторые файлы, которые могут быть в любой форме порядка байтов (большой или маленький). Интерпретатор Perl умирает, если я использую одну кодировку и анализирую другую.
open (my $fh, "<:raw:encoding(UTF-16LE):crlf", $ARGV[0]) or die cannot open file for reading : $! \n";
или же
open (my $fh, "<:raw:encoding(UTF-16BE):crlf", $ARGV[0]) or die cannot open file for reading : $! \n";
вывод (для файла в LE и кодировки perl, являющейся BE)
UTF-16BE:Malformed HI surrogate dc00 at toASCII.pl line 123.
2 ответа
Большинство файлов UTF-16le являются допустимыми файлами UTF-16be и наоборот. Например, нет никакого способа сказать, если 0A 00
указывает U+000A (UTF-16le) или U+0A00 (UTF-16be). Итак, при условии, что нет спецификации, вы должны догадаться.
Возможные эвристики (в порядке убывания надежности):
- U + FFFE не является символом (гарантировано).
- Если файл начинается с
FF FE
тогда это должен быть UTF-16le. - Если файл начинается с
FE FF
тогда это должен быть UTF-16be.
- Если файл начинается с
- Если файл не является допустимым UTF-16be, то он должен быть UTF-16le.
- Если файл не является допустимым UTF-16le, то это должен быть UTF-16be.
- Если файл содержит не-символы при декодировании с использованием UTF-16be, то он должен быть UTF-16le.
- Если файл содержит не-символы при декодировании с использованием UTF-16le, то это должен быть UTF-16be.
- U + 0A00 в настоящее время не назначено, но U+000A (LINE FEED) довольно распространено.
U + 0D00 в настоящее время не назначен, но U+000D (ВОЗВРАТ КАРРИДЖА) довольно распространен.- Если файл содержит
0A 00
или же0D 00
тогда это наверное UTF-16le. - Если файл содержит
00 0A
или же00 0D
тогда это наверное UTF-16be.
- Если файл содержит
- Если файл содержит неназначенные символы при декодировании с использованием UTF-16be, то это, вероятно, UTF-16le.
- Если файл содержит неназначенные символы при декодировании с использованием UTF-16le, то это, вероятно, UTF-16be.
- Эвристика основана на знании формата файла. ( Пример)
- Файл, скорее всего, содержит больше символов ASCII, чем число символов U+xx00
- Если файл содержит много
xx 00
и мало00 xx
тогда это наверное UTF-16le. - Если файл содержит много
00 xx
и малоxx 00
тогда это наверное UTF-16be.
- Если файл содержит много
Заметки:
- № 4 и № 5 говорят "это, вероятно," вместо "это должно быть", потому что то, что не назначено сегодня, может быть назначено завтра.
- № 3 включает в себя № 1, но № 1 является дешевым тестом.
- № 5 включает в себя № 4, но № 4 почти так же надежен, как № 5, не поддерживая длинный список неназначенных символов, который меняется со временем.
Вы можете сделать это, используя файл :raw
, выполните некоторые или все вышеупомянутые тесты для определения кодировки, затем используйте decode
а также s/\r\n/\n/g
,
Вы не показываете никакого кода, но в целом невозможно определить, что такое файл с порядком байтов, если вы не знаете, какие значения вы должны читать из файла. Многие форматы файлов, например, резервируют несколько байтов в начале, чтобы указать, какой это формат, и если это относится к данным, с которыми вы имеете дело, то вы можете просто read
эти байты, и измените режим открытия, если вы не получите то, что ожидали
Кроме того, поскольку ваша программа умирает, если выбран неправильный формат, вы можете использовать это для проверки правильности выбранного формата. Как-то так должно подойти
my $file = $ARGV[0];
open my $fh, '<:raw:encoding(UTF-16LE):crlf', $file or die $!;
eval { do_stuff_that_may_crash() };
if ( $@ ) {
if ( $@ =~ /Malformed HI surrogate/ ) {
open my $fh, '<:raw:encoding(UTF-16BE):crlf', $file or die $!;
do_stuff_that_may_crash();
}
else {
die $@;
}
}
но так как это звучит как do_stuff_that_may_crash()
это почти вся ваша программа, вы, вероятно, должны найти лучший критерий