【问题标题】:Encode::Guess:guess_encoding gives different results in different contextsEncode::Guess:guess_encoding 在不同的上下文中给出不同的结果
【发布时间】:2019-03-08 14:32:05
【问题描述】:

我有以下子程序打开一个文本文件并尝试确保其编码是 UTF-8、ISO-8859-15 或 ASCII 之一。

我遇到的问题是交互式和非交互式使用中的不同行为。

  • 当我以交互方式运行包含 UTF-8 行的文件时,$decoder 正如预期的那样是一个引用对象,其name 为该行返回 utf8。

  • 非交互方式(因为它作为 subversion 提交挂钩的一部分运行)guess_encodingutf8 检查行返回值 utf8 or iso-8859-15 的标量字符串,为其他两行返回 iso-8859-15 or utf8 .

我一辈子都无法弄清楚行为差异的来源。如果我强制open 的编码为<:encoding(utf8),它将毫无疑问地接受每一行作为 UTF-8。

问题是我不能假设它收到的每个文件都是 UTF-8,所以我不想强制编码作为解决方法。另一种可能的解决方法是解析标量文本,但这看起来很混乱,尤其是当它似乎在交互式上下文中正常工作时。

在 shell 中,我尝试覆盖 $LANG(因为未设置非交互式,也未设置任何 LC_ 变量),但交互式版本仍然可以正常运行。

报告$Encode::Guess::NoUTFAutoGuess 的已注释掉的行在注释时在交互式和非交互式使用中都返回 0。

最终,我们试图阻止的一件事是在我们的存储库中使用 UTF-16 或其他宽字符编码(因为我们的一些工具不能很好地使用它):我认为寻找白色- 编码列表比寻找编码黑名单更容易。

sub checkEncoding
{
    my ($file) = @_;

    my ($b1, $b2, $b3);
    my $encoding = "";
    my $retval = 1;
    my $line = 0;

    say("Checking encoding of $file");
    #say($Encode::Guess::NoUTFAutoGuess);
    open (GREPFILE, "<", $file);
    while (<GREPFILE>) {
            chomp($_);
            $line++;

            my $decoder = Encode::Guess::guess_encoding($_, 'utf8');
            say("A: $decoder");
            $decoder = Encode::Guess::guess_encoding($_, 'iso-8859-15') unless ref $decoder;
            say("B: $decoder");
            $decoder = Encode::Guess::guess_encoding($_, 'ascii') unless ref $decoder;
            say("C: $decoder");

            if (ref $decoder) {
                    $encoding = $decoder->name;
            } else {
                    say "Mis-identified encoding '$decoder' on line $line: [$_]";
                    my $z = unpack('H*', $_);
                    say $z;
                    $encoding = $decoder;
                    $retval = 0;
            }

            last if ($retval == 0);
    }
    close GREPFILE;

    return $retval;
}

【问题讨论】:

  • 我以为你应该输入 Encode::Guess 整个数据集(文件)以获得最准确的结果?对我来说,您似乎正在尝试分别猜测每一行的编码。即使是 UTF-8 文件,有些行可能没有字节组合,使它看起来像 UTF-8,所以对于那些行,猜测也会考虑 ASCII 或 ISO-8859-15。
  • 我也认为你应该在open() 中使用&lt;:raw
  • 从已知的角度来看,ISO-8859-1=Yes。没有任何字节值或值序列与 ISO-8859-1 不兼容。
  • 等等,在一个地方你说ISO-8859-1,但在其他地方你说-15-1 是错字吗?
  • 大家好,感谢 cmets - 对整个文件进行更改并给出guess_encoding()。然而,核心问题仍然存在:当以交互方式运行时,$decoder 最终成为引用类型,当通过 SVN 挂钩以非交互方式运行时,$decoder 最终成为标量。我想了解为什么会有所不同?

标签: perl character-encoding


【解决方案1】:

无需猜测。 UTF-8、ISO-8859-1和US-ASCII的具体选项可以使用Encoding::FixLatinfix_latin。我是virtually guaranteed to succeed

也就是说,我认为在 OP 中使用 ISO-8859-1 是对 ISO-8859-15 的错字。

fix_latin 使用的方法对 ISO-8859-15 和 ISO-8859-1 一样有效。这只是将_init_byte_map 替换为以下内容的问题:

sub _init_byte_map {
    foreach my $i (0x80..0xFF) {
        my $byte = chr($i);
        my $utf8 = Encode::from_to($byte, 'iso-8859-15', 'UTF-8');
        $byte_map->{$byte} = $utf8;
    }
}

或者,如果您愿意假设数据全部是一种或另一种编码(而不是混合),您也可以使用以下方法:

my $text;
if (!eval {
   $text = decode("UTF-8", $bytes, Encode::FB_CROAK|Encode::LEAVE_SRC);
   1  # No exception
}) {
   $text = decode("ISO-8859-15", $bytes);
}

请记住,US-ASCII 是 UTF-8 和 ISO-8859-15 的真子集,因此不需要特殊处理。

【讨论】:

  • 添加回答。
  • 嗨,我最终不想做decode,但如果这将是检测编码的最可靠方法,我将使用它(预期的行为是抛出一个不可接受的编码错误,不能在编码之间转换)。我主要关心的是guess_encoding 在不同上下文中的不同行为(即交互式与非交互式)。
猜你喜欢
  • 1970-01-01
  • 2018-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-23
相关资源
最近更新 更多