处理无效的 UTF-8

读无效的 UTF-8

在读取 UTF-8 编码数据时,重要的是要注意 UTF-8 编码数据可能无效或格式错误。这些数据通常不应被你的程序接受(除非你知道自己在做什么)。当意外遇到格式错误的数据时,可以考虑不同的操作:

  • 打印堆栈跟踪或错误消息,并正常中止程序,或
  • 在出现格式错误的字节序列的位置插入替换字符,向 STDERR 输出警告消息并继续读取,因为没有发生任何事情。

默认情况下,Perl 会告诉你关于编码故障的信息,但它不会中止你的程序。你可以通过使 UTF-8 警告致命来使你的程序中止,但要注意致命警告中的警告

以下示例将编码 ISO 8859-1 中的 3 个字节写入磁盘。然后它尝试再次读取字节作为 UTF-8 编码数据。其中一个字节 0xE5 是无效的 UTF-8 单字节序列:

use strict;
use warnings;
use warnings FATAL => 'utf8';

binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';
my $bytes = "\x{61}\x{E5}\x{61}";  # 3 bytes in iso 8859-1: aåa
my $fn = 'test.txt';
open ( my $fh, '>:raw', $fn ) or die "Could not open file '$fn': $!";
print $fh $bytes;
close $fh;
open ( $fh, "<:encoding(utf-8)", $fn ) or die "Could not open file '$fn': $!";
my $str = do { local $/; <$fh> };
close $fh;
print "Read string: '$str'\n";

该程序将以致命的警告中止:

utf8 "\xE5" does not map to Unicode at ./test.pl line 10.

第 10 行是第二行,当尝试从文件中读取一行时,错误发生在 <$fh> 行的部分。

如果你没有在上述程序中发出致命警告,Perl 仍会打印警告。但是,在这种情况下,它会尝试通过将四个字符\xE5 插入流中来从错误字节 0xE5 中恢复,然后继续下一个字节。结果,该程序将打印:

Read string: 'a\xE5a'