如何在 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
编码),而是使用decode
和utf-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?的主要内容,如果未能解决你的问题,请参考以下文章