如何从 Text::CSV 输出 utf8?

How to make the output from Text::CSV utf8?

我有一个 CSV 文件,例如 win.csv,其文本编码为 windows-1252。首先我用iconv在utf8中制作它。

$iconv -o test.csv -f windows-1252 -t utf-8 win.csv

然后我使用以下 Perl 脚本 (utfcsv.pl) 读取转换后的 CSV 文件。

#!/usr/bin/perl 
use utf8;
use Text::CSV;
use Encode::Detect::Detector;

my $csv = Text::CSV->new({ binary => 1, sep_char => ';',});
open my $fh, "<encoding(utf8)", "test.csv";

while (my $row = $csv->getline($fh)) { 
  my $line = join " ", @$row;
  my $enc = Encode::Detect::Detector::detect($line);
  print "($enc) $line\n";
}

$csv->eof || $csv->error_diag();
close $fh;
$csv->eol("\r\n");
exit;

然后输出如下。

(UFT-8) .........
() .....

即检测所有行的编码为UTF-8(或ASCII)。但是实际输出的好像不是UTF-8。事实上,如果我将输出保存在文件中

$./utfcsv.pl > output.txt

然后output.txt的编码检测为windows-1252.

问:如何获取UFT-8的输出文本?

备注:

  1. 环境:openSUSE 13.2 x86_64,perl 5.20.1
  2. 我没有使用Text::CSV::Encoded,因为安装失败。 (因为test.csv是UTF-8转换的,所以用Text::CSV::Encoded很奇怪。)
  3. 我使用以下脚本来检查编码。 (我也用它来找出初始CSV文件的编码win.csv。)

.

#!/usr/bin/perl 
use Encode::Detect::Detector;
open my $in,  "<","$ARGV[0]" || die "open failed";
while (my $line = <$in>) {
  my $enc = Encode::Detect::Detector::detect($line);
  chomp $enc;
  if ($enc) {
    print "$enc\n";
  }
}

您已经设置了输入文件句柄的编码(顺便说一句,应该是 <:encoding(utf8) -- 注意冒号)但是您还没有指定 output 通道,因此 Perl 会将未编码的字符值发送到 output

适合单个字节的字符的 Unicode 值——0 到 0x7F 之间的基本拉丁语 (ASCII) 和 0x80 到 0xFF 之间的 Latin-1 增补——与 Windows 代码非常相似第 1252 页。特别是带有分音符的小写字母 u 在 Unicode 和 CP1252 中都是 0xFC,因此如果输出未编码的文本将看起来像 CP1252,而不是双字节序列 0xC3 0xBC,它是在 UTF 中编码的相同代码点-8

如果您在 STDOUT 上使用 binmode 设置编码,则数据将正确输出,但最简单的方法是像这样使用 open pragma

use open qw/ :std :encoding(utf-8) /;

这将为 STDIN、STDOUT 和 STDERR 以及任何新打开的文件句柄设置编码。这意味着您不必在打开 CSV 文件时指定它,您的代码将如下所示

请注意,我还添加了 use strictuse warnings,它们在任何 Perl 程序中都是必不可少的。我也有 使用 autodie 消除了检查所有 IO 操作状态的需要,我利用了 Perl 通过在元素之间放置 space 来在双引号内插入数组的方式,这避免了需要join 电话

#!/usr/bin/perl

use utf8;
use strict;
use warnings 'all';
use open qw/ :std :encoding(utf-8) /;
use autodie;

use Text::CSV;

my $csv = Text::CSV->new({ binary => 1, sep_char => ';' });

open my $fh, '<', 'test.csv';

while ( my $row = $csv->getline($fh) ) {
    print "@$row\n";
}

close $fh;