【问题标题】:How to detect malformed UTF-8 at the end of the file?如何在文件末尾检测格式错误的 UTF-8?
【发布时间】:2016-08-04 06:58:14
【问题描述】:

我试图在读取包含无效 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) 默默忽略了。

【问题讨论】:

    标签: perl utf-8


    【解决方案1】:

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

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

    我不确定,您可能希望您的开放编码以 :raw 开头,或者改为使用 binmode $fh, ':raw',我从来没有过多关注图层本身,因为它通常可以正常工作。我知道这个代码块适用于你的测试用例:)

    【讨论】:

      【解决方案2】:

      我不确定你在问什么。要检测字符串中的编码错误,您可以简单地尝试对字符串进行解码。至于写入文件时出错,可能close返回错误,或者您可以使用chomp($_); print($fh "$_\n");(无论如何,unix文本文件应该始终以换行符结尾)。

      【讨论】:

      • 所以首先以字节形式读取文件,然后使用Encode::decode('utf-8', $raw, Encode::FB_QUIET) 将其作为字符串单步执行?在每次失败时重复数据的剩余部分。作为最后的手段是的,但我宁愿避免设计自己的解码算法只是为了打印警告。也许我应该针对Encode 提交错误报告?
      【解决方案3】:
      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 '
      

      【讨论】:

      • 编写文件的代码部分不是我试图说明的一般问题的一部分。它只是被添加来创建一个MCVE。您可以假设文件本身不能更改或重写为相同的文件名。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-23
      • 2011-02-09
      • 1970-01-01
      • 1970-01-01
      • 2011-03-16
      相关资源
      最近更新 更多