代码之家  ›  专栏  ›  技术社区  ›  Jonah Bishop

为什么在读取UTF-8文件时BOM仍然存在?

  •  1
  • Jonah Bishop  · 技术社区  · 9 年前

    我正在尝试用Perl读取一些UTF-8编码的CSV文件(至少我相信它们就是这样),并将它们全部写入一个更大的文件中。这是我的脚本:

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    open my $out, '>:encoding(UTF-8)', "output.csv" or die "Cannot open output.csv: $!";
    
    my @files = <*.csv>;
    foreach(@files) {
        next if $_ =~ m/^output.csv$/;
    
        print "Parsing $_\n";
    
        open my $in, '<:encoding(UTF-8)', $_ or die "Cannot open $_: $!";
        while(<$in>) {
            chomp;
            next if m/^\s*$/;
            print $out "$_\n";
        }
        close $in;
    }
    close $out;
    

    说完就完成了,每个文件的内容都以 BOM ,这意味着BOM表显示为每个文件数据的前三个字节。不应使用 >:encoding(UTF-8) 指令已经取消了BOM?为什么它会继续出现在我的输出中?

    1 回复  |  直到 9 年前
        1
  •  6
  •   Community CDub    4 年前

    UTF-8是一种基于字节的编码,因此字节顺序无关,初始字节顺序标记(BOM)是不必要的,通常不鼓励在UTF-8数据中使用。但是它的有效性和功能取决于当前的应用程序,因此Perl不能毫无疑问地将其从数据中剥离出来

    Unicode BOM表字符 U+FEFF 与共享编码 零宽度不间断空间 字符,因此,如果布局是唯一的问题,则如果保留在中,则不应引起问题,即使多个源串联在一起,使其出现在数据流的中间

    在大多数文件应用程序中,UTF-8数据源被透明处理,因此仅包含7位ASCII数据的文件与相同数据的UTF-8编码相同。此类数据 不能 包含BOM表,因为它会影响透明度。例如 shebang公司 #! UTF-8编码的shell命令文件开头的行不能前面有字节顺序标记,因为shell将无法识别它

    可以从BOM表字符的开始处删除BOM表字符 解码的 Unicode数据,无论来源如何

    s/\A\N{BOM}//
    

    当然,这个角色可以被删除 自始至终 通过使用全局替换 \A 锚被移除,或更整洁

    tr/\N{BOM}//d
    


    使现代化

    字符流被读取为 字节 ,在16位或32位编码中,您需要知道首先出现的是最低有效字节(小端)还是最高有效字节(大端),以便您知道如何将这些字节组合成多字节字符

    BOM表字符为 总是 U+FEFF 它的要点是这是不变的。所以如果我从文件中读取前两个字节,它们是 FF FE 按照这个顺序,我知道整个文件是UTF-16(或UTF-32)编码的,最低有效字节后面是最高有效字节,或小尾数,然后我可以正确地解释文件的其余部分

    但是字节顺序在基于字节的编码中是没有意义的。每个字符由一个或多个字节的序列表示,并且数据是相同的 不管其原始系统的结尾如何 .BOM字符 U+FEFF 以UTF-8编码为三个十六进制字节 EF , BB , BF 按照这个顺序,这是不变的

    这个 File::BOM 单元

    在我看来, File::BOM 使一个简单的概念变得不必要地复杂

    我认为,如果您必须处理许多不同的Unicode文件,这些文件具有不同的编码,来自具有不同结尾的平台,这是很有用的,但在这种情况下,每行文本末尾的记录分隔符的字符序列的变化可能会更大的问题

    只要在打开文件之前知道文件的编码,就应该打开它并根据该标准进行读取。如果数据中存在BOM表字符是一个问题,那么只需使用 s/// tr///d 以将其移除。但请记住,在所有符合Unicode的系统上,BOM字符应该被透明地忽略