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?
答案 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);