如何在 Perl 中清理无效的 UTF-8?

Posted

技术标签:

【中文标题】如何在 Perl 中清理无效的 UTF-8?【英文标题】:How do I sanitize invalid UTF-8 in Perl? 【发布时间】:2011-09-08 05:29:36 【问题描述】:

我的 Perl 程序从磁盘文件中获取一些文本作为输入,将其包装在一些 XML 中,然后将其输出到 STDOUT。输入名义上是 UTF-8,但有时会插入垃圾。我需要清理输出,以免发出无效的 UTF-8 八位字节,否则下游消费者 (Sphinx) 会爆炸。

至少我想知道如果数据是无效的,这样我就可以避免传递它;理想情况下,我可以只删除有问题的字节。但是,使用 perl 5.12 启用我能找到的所有宿命论并不能完全实现(FWIW,use v5.12; use warnings qw( FATAL utf8 ); 有效)。

我在序列"\xFE\xBF\xBE" 方面特别有问题。如果我创建一个仅包含这三个字节 (perl -e 'print "\xEF\xBF\xBE"' > bad.txt) 的文件,则尝试使用模式 :encoding(UTF-8) 读取文件时出现utf8 "\xFFFE" does not map to Unicode 错误,但仅在 5.14.0 下。 5.12.3 和更早的版本非常适合阅读和稍后编写该序列。我不确定它从哪里得到\xFFFE(非法反向BOM),但至少有一个投诉与Sphinx 一致。

很遗憾,decode_utf8("\xEF\xBF\xBE", 1) 在 5.12 或 5.14 下不会导致错误。我更喜欢不需要编码 I/O 层的检测方法,因为这只会给我留下错误消息,并且无法清理原始八位字节。

我确信我需要处理更多的序列,但仅处理这一序列将是一个开始。所以我的问题是:我可以在 5.14 之前用 perl 可靠地检测到这种问题数据吗?什么替换例程通常可以将几乎 UTF-8 清理为严格的 UTF-8?

【问题讨论】:

【参考方案1】:

你有一个 utf8 字符串,其中包含一些无效的 utf8...

这会将其替换为默认的“坏字符”。

use Encode qw(decode encode);

my $octets    = decode('UTF-8', $malformed_utf8, Encode::FB_DEFAULT);

my $good_utf8 = encode('UTF-8', $octets,         Encode::FB_CROAK);

【讨论】:

【参考方案2】:

您应该阅读Encode 文档中的UTF-8 vs. utf8 vs. UTF8 section。

总而言之,Perl 有两种不同的 UTF-8 编码。它的本机编码称为utf8,基本上允许任何代码点,而不管 Unicode 标准对该代码点有何规定。

另一种编码称为utf-8(又名utf-8-strict)。这仅允许被 Unicode 标准列为合法的代码点进行交换。

"\xEF\xBF\xBE",当解释为 UTF-8 时,解码为代码点 U+FFFE。但是根据 Unicode 进行交换是不合法的,因此对此类事情要求严格的程序会抱怨。

不要使用decode_utf8(使用宽松的utf8 编码),而是使用decodeutf-8 编码。并阅读Handling Malformed Data 部分,了解处理或投诉问题的不同方式。

更新:似乎某些版本的 Perl 不会抱怨 U+FFFE,即使使用 utf-8-strict 编码也是如此。这似乎是一个错误。您可能只需要构建一个 Sphinx 抱怨的代码点列表并手动过滤掉它们(例如使用tr)。

【讨论】:

代码示例:***.com/questions/3735721/… 谢谢,这很有帮助。由于 XML 中允许的 Unicode 字符定义明确,tr[\x9\xA\xD\x20-\xD7FF\xE000-\xFFFD\x10000-\x10FFFF][]cd 看起来可以工作,至少在 5.14 中是这样。我发现一个完全不同的建议根本不依赖于新的 perl:iconv -c --from UTF-8 --to UTF-8

以上是关于如何在 Perl 中清理无效的 UTF-8?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Perl 输出 UTF-8?

如何在 Perl 中将输入文件转换为 UTF-8 编码?

Perl:如何解析无效的 XML 文档? [复制]

Paypal IPN:UTF-8 中的无效字节序列

如何在perl中用'*'替换字符串中的所有字符

如何使用来自 AngularJS 控制器的无效输入正确清理表单?