【问题标题】:"Unrecognized character \xEF" Error with BOM-Free File无 BOM 文件出现“无法识别的字符 \xEF”错误
【发布时间】:2014-07-26 19:51:53
【问题描述】:

我目前正在使用 Perl 和模块 Text::CSV 从 CSV 文件中提取文本。

每个 CSV 文件都有引号分隔每个字段。文本被保存到独立的文本文件中,标签分隔成列。我可以从文本文件中调用和打印每一列没有问题,但是当我尝试在循环中使用这些值时,我收到错误Unrecognized character \xEF

我的代码示例如下:

#!/usr/bin/perl

use strict;
use warnings;
use Text::CSV;

#### Match ligand data with GPCR interaction data ####
my $csv = Text::CSV->new();
my $file = $ARGV[0];
open (FILE, "<$file");
open (OUT, ">new_$file");
while (my $line2 = <FILE>)
{
    binmode(STDOUT, ":utf8");
    if ($line2 =~ /^(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)$/)
    {
        #### Data from filtered1.txt ####
        my $up_fil = $1;
        my $ligid_fil = $2;
        my $units_fil = $3;
        my $low_fil = $4;
        my $median_fil = $5;
        my $upper_fil = $6;
        my $ref = $7;

        #### Convert negative log affinity values to normal ####
        my $activity = $units_fil;
        $activity =~ s/p//;
        my $value;

        if ($median_fil ne "")
        { 
            $value = $median_fil;
            $value = (10**-$median_fil)/(10**-9);
        }
        elsif ($low_fil ne "" and $upper_fil ne "") 
        {
            my $lower = $low_fil;
            $lower = (10**-$low_fil)/(10**-9);
            my $upper = $upper_fil;
            $upper = (10**-$upper_fil)/(10**-9);
            $value = "$upper - $lower";
        }
        else
        {
            $value = "n/a";
        }


        #### Match entries from filtered1.txt with ligands.csv ####
        open (LIG, "<ligands.csv");
        while (my $line3 = <LIG>)
        {
            $csv->parse($line3);
            my @ligand_fields = $csv->fields();
            if (!$ligand_fields[14]) { next; }
            if ($ligand_fields[0] eq $ligid_fil)
            {
                #print OUT "$ligand_fields[14]\t$ligand_fields[13]\t$up_fil\t$ligid_fil\t$activity\t$value\t$ref\n";
                print "$ligand_fields[14]\t$ligand_fields[13]\t$up_fil\t$ligid_fil\t$activity\t$value\t$ref\n";
                next;
            }
        }
            close LIG;
        }
    }
    close FILE;
    close OUT;

我也尝试过按照以下方式使用正则表达式,但无济于事。

# remove BOM
${$self->{CODE}} =~ s/^(?:
    \xef\xbb\xbf     |
    \xfe\xff         |
    \xff\xfe         |
    \x00\x00\xfe\xff |
    \xff\xfe\x00\x00
)//x;

原始 CSV 文件似乎没有任何 BOM,因此我怀疑 Text::CSV 可能在解析和返回值时创建它。我希望这是对问题的足够清楚的解释,如果需要,我可以提供更多细节。提前感谢您提供的任何建议。

【问题讨论】:

  • 您读取编码字节,然后告诉 STDOUT 它们是要转换为 UTF-8 的 Unicode 代码点。解码您的输入文件以获得 Unicode 代码点!
  • 感谢您的编辑,米勒。另一方面,我发现出了什么问题。原来 if 和($median_fil ne "") 之间有一个奇怪的类似空格的字符,导致错误。我删除了空间,添加了一个新空间,瞧,错误消失了。无论如何,感谢您的帮助!

标签: perl csv byte-order-mark


【解决方案1】:

Text::CSV 的文档表明您几乎肯定应该使用二进制模式。

 my $csv = Text::CSV->new ( { binary => 1 } )  # should set binary attribute.
                 or die "Cannot use CSV: ".Text::CSV->error_diag ();

来自https://metacpan.org/pod/Text::CSV#SYNOPSIS

您可能还想看看Text::CSV::Encoded

我还看到您在 STDOUT 上设置了 :utf8 的 binmode。这样做有几个问题:

  1. 您每次都在不必要地围绕循环进行设置
  2. :utf8 binmode 没有很好的错误检查,你应该改用:encoding(UTF-8)

字节 0xEF 可以出现在 UTF-8 字节序列中,但仅在非常特殊的情况下,它太高(> 0x7F)而不是单个字符。但是在 Perl 中,\xEF\x{ef} 不是指字节 0xEF,而是指 Unicode 代码点 U+00EF,它在 UTF-8 中表示为 0xC3 0xAF。您可以在 Unicode/UTF-8 字符表中看到这一点,例如 http://www.utf8-chartable.de/

$ perl -E 'binmode STDOUT, ":encoding(UTF-8)"; say "\xEF";'
ï

所以我认为这就是为什么您删除 BOM 的正则表达式不起作用的原因。

我建议使用three argument open'&lt;:encoding(UTF-8)''&gt;:encoding(UTF-8)' 打开所有输入和输出文件,并以二进制模式使用Text::CSV,以获得最佳效果。

【讨论】:

  • 一个字节 \xEF 可以出现在有效的 UTF-8 中,但前提是后面跟在 \x80..\xBF 范围内的 2 个连续字节。 (以下字节可能还有其他限制,但这是最基本的限制。)
  • 啊,我明白我的错误了。在我的 Perl 单行 (perl -E 'binmode STDOUT, ":encoding(UTF-8)"; say "\xEF";') 中,“\xEF”指的是 Unicode 代码点 U+00EF(即“ï”)而不是字节 EF,正如您所说,它不是有效的 UTF-8 字符。我会更新我的答案,谢谢。
猜你喜欢
  • 1970-01-01
  • 2021-01-13
  • 2019-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-12
  • 1970-01-01
  • 2014-02-17
相关资源
最近更新 更多