如何在文件末尾检测格式错误的UTF-8?

时间:2016-08-04 06:58:15

标签: perl utf-8

我正在尝试在读取文件(应该包含有效的UTF-8)包含无效的UTF-8时打印警告消息。但是,如果无效数据位于文件末尾,则无法输出任何警告。以下MVCE创建一个包含无效UTF-8数据的文件(文件的创建与一般问题无关,它只是在这里添加以生成MVCE):

use feature qw(say);
use strict;
use warnings;

binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';

my $bytes = "\x{61}\x{E5}\x{61}";  # 3 bytes in iso 8859-1: aåa
test_read_invalid( $bytes );
$bytes = "\x{61}\x{E5}";  # 2 bytes in iso 8859-1: aå
test_read_invalid( $bytes );

sub test_read_invalid {
    my ( $bytes ) = @_;
    say "Running test case..";
    my $fn = 'test.txt';
    open ( my $fh, '>:raw', $fn ) or die "Could not open file '$fn': $!";
    print $fh $bytes;
    close $fh;
    my $str = '';
    open ( $fh, "<:encoding(utf-8)", $fn ) or die "Could not open file '$fn': $!";
    $str = do { local $/; <$fh> };
    close $fh;
    say "Read string: '$str'\n";
}

输出结果为:

Running test case..
utf8 "\xE5" does not map to Unicode at ./p.pl line 22.
Read string: 'a\xE5a'

Running test case..
Read string: 'a'

在最后一个测试用例中,文件末尾的无效字节似乎被PerlIO层:encoding(utf-8)默默忽略。

3 个答案:

答案 0 :(得分:2)

基本上你所看到的是perlIO系统试图处理在utf-8序列中间结束的块读取。所以原始字节缓冲区仍然有你想要的无效字节,但编码缓冲区还没有那个内容,因为它还没有正确解码,它希望以后能找到另一个字符。您可以通过弹出编码层并进行另一次读取并检查长度来检查这一点。

binmode $fh, ':pop';
my $remainder = do { local $/; <$fh>};
die "Unread Characters" if length $remainder;

我不确定,你可能想让你的开放编码开始于:raw或者做binmode $ fh,':raw'而是,我从来没有过多关注图层本身,因为它通常只是起作用。我知道这个代码块适用于您的测试用例:)

答案 1 :(得分:1)

我不确定你在问什么。要检测字符串中的编码错误,您只需尝试解码字符串即可。至于从写入文件中获取错误,可能close会返回错误,或者您可以使用chomp($_); print($fh "$_\n");(因为unix文本文件应始终以换行符结尾)。

答案 2 :(得分:0)

open ( my $fh, '>:raw', $fn ) or die "Could not open file '$fn': $!";
#the end of the file need a single space to find a invalid UTF-8 characters. 
print $fh "$bytes ";

输出:

Running test case..
utf8 "\xE5" does not map to Unicode at ent.pl line 23.
Read string: 'a\xE5a '

Running test case..
utf8 "\xE5" does not map to Unicode at ent.pl line 23.
Read string: 'a\xE5a '