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

时间:2011-06-04 02:49:06

标签: perl utf-8 sanitization

My 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?

2 个答案:

答案 0 :(得分:21)

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

总而言之,Perl有两种​​不同的UTF-8编码。它的本机编码称为utf8,并且基本上允许任何代码点,无论Unicode标准对该代码点的描述如何。

其他编码称为utf-8(a.k.a。utf-8-strict)。这仅允许按Unicode标准列为合法交换的代码点。

"\xEF\xBF\xBE",当解释为UTF-8时,解码为代码点U+FFFE。但根据Unicode,这对于交换来说是不合法的,所以对这类事情严格的程序会抱怨。

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

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

答案 1 :(得分:3)

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

将其替换为默认的'bad char'。

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);