Как проверить в 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). Итак, при условии, что нет спецификации, вы должны догадаться.

Возможные эвристики (в порядке убывания надежности):

  1. 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.
  2. 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.
  3. Эвристика основана на знании формата файла. ( Пример)
  4. Файл, скорее всего, содержит больше символов 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() это почти вся ваша программа, вы, вероятно, должны найти лучший критерий

Другие вопросы по тегам