4

实际上我必须解析一些可以是任何形式的字节序(大或小)的文件。如果我使用一种编码并解析另一种编码,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.
4

2 回答 2

5

大多数 UTF-16le 文件都是有效的 UTF-16be 文件,反之亦然。例如,无法判断是0A 00U+000A (UTF-16le) 还是 U+0A00 (UTF-16be)。因此,假设没有BOM,您必须猜测。

可能的启发式方法(按可靠性降序排列):

  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 00or 0D 00,那么它可能是 UTF-16le。
    • 如果文件包含00 0Aor 00 0D,那么它可能是 UTF-16be。
    • 如果文件在使用 UTF-16be 解码时包含未分配的字符,那么它可能是 UTF-16le。
    • 如果文件在使用 UTF-16le 解码时包含未分配的字符,那么它可能是 UTF-16be。
  3. 基于文件格式知识的启发式方法。(
  4. 一个文件可能包含比字符数更多的 ASCII 字符 U+xx00
    • 如果文件包含 manyxx 00和many 00 xx,那么它可能是 UTF-16le。
    • 如果文件包含 many00 xx和many xx 00,那么它可能是 UTF-16be。

笔记:

  • #4 和 #5 说“它可能”而不是“它必须是”,因为今天未分配的内容可能会在明天分配。
  • #3 包括 #1,但 #1 是一个廉价的测试。
  • #5 包括 #4,但 #4 几乎与 #5 一样可靠,而无需维护一长串随时间变化的未分配字符。

您可以使用 slurp 文件:raw,对其执行部分或全部上述测试以确定编码,然后使用decodeand s/\r\n/\n/g

于 2016-12-16T01:14:05.280 回答
1

您没有显示任何代码,但一般来说,除非您知道应该从文件中读取哪些值,否则无法判断文件的字节顺序。例如,许多文件格式在开头保留几个字节以指示格式是什么,如果这适用于您正在处理的数据,那么您可以只使用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()几乎是您的所有程序,您可能应该找到一个更好的标准

于 2016-12-15T21:03:06.353 回答