在DATA和ARGV文件句柄上进行Perl UTF-8编码

时间:2014-08-24 15:11:59

标签: perl unicode utf-8

我有一些带有大量Unicode希伯来语和希腊语的文本文件需要包含在HTML <span class ="hebrew">...</span>元素中。这些文件属于已运行多年的项目。

大约八年前,我们成功地使用了这个Perl脚本来完成这项工作。

#!/usr/bin/perl

use utf8;

my $table = [
  {
    FROM  => "\\x{0590}",
    TO    => "\\x{05ff}",
    REGEX => "[\\x{0590}-\\x{05ff}]",
    OPEN  => "<span class =\"hebrew\">",
    CLOSE => "</span>",
  },
  {
    FROM  => "\\x{0370}",
    TO    => "\\x{03E1}",
    REGEX => "[\\x{0370}-\\x{03E1}]|[\\x{1F00}-\\x{1FFF}]",
    OPEN  => "<span class =\"greek\">",
    CLOSE => "</span>",
  },
];

binmode(STDIN,":utf8");
binmode(STDIN,"encoding(utf8)");

binmode(STDOUT,":utf8");
binmode(STDOUT,"encoding(utf8)");

while (<>) {

  my $line = $_;

  foreach my $l (@$table) {

    my $regex          = $l->{REGEX},
    my ($from, $to)    = ($l->{FROM},$l->{TO});
    my ($open, $close) = ($l->{OPEN},$l->{CLOSE});

    $line =~ s/(($regex)+(\s+($regex)+)*)/$open\1$close/g;
  }

  print $line;
}

扫描文本文件,查找已定义的Unicode范围,并插入相应的span包装器。

我还没有使用过这个脚本一段时间,现在我需要处理更多的文本文件。但不知何故,Unicode没有被保留:Unicode文本被破坏而不是被<span>标签包裹。

在我继续之前,我需要帮助修复。

这里有一些示例输入

Mary had a little כֶּבֶשׂ, its fleece was white as χιών. And πάντα that Mary went, the כֶּבֶשׂ was sure to go.

以下是我的输出结果:

Mary had a little ×Ö¼Ö¶×ֶש×, its fleece was white as ÏιÏν. And ÏάνÏα that Mary went, the ×Ö¼Ö¶×Ö¶×©× was sure to go.

就在我使用Linux Mint 13 LTS的机器上。我的其他操作系统是Ubuntu 14.04。 Perl版本报告为v.5.14.2。我正在运行这样的脚本

perl uconv.pl infile.txt > outfile.txt

我不确定发生了什么,并且尽管查看了很多 Stack Overflow 问题和答案(例如this one),我&# 39;毫无智慧。也许我需要设置一些环境变量?或者该脚本现在已弃用?还是...?

1 个答案:

答案 0 :(得分:5)

你的输出很好。 Perl正在为UTF-8编码的字符串打印正确的字节序列。

例如,第一个希伯来语单词כֶּבֶשׂ包含这七个unicode字符

05DB   05BC   05B6   05D1   05B6   05E9   05C2
kaf    dagesh segol  bet    segol  shin   sin dot

以UTF-8编码为十四个字节(每个字符两个)

[D7 9B] [D6 BC] [D6 B6] [D7 91] [D6 B6] [D7 A9] [D7 82]

,这是您显示的格式错误的字符串的内容。

问题不在于程序打印错误的字符,而是用于检查输出的任何内容都不是期望UTF-8。


<强>更新

看起来问题出在ARGV上,而不是STDIN。从空文件句柄读取实际上是从ARGV读取的,因此在STDIN上使用binmode设置UTF-8 Perl IO层,就像您所做的那样,没有效果。此外,您无法以相同的方式设置ARGV的模式,因为它尚未打开。

但您可以使用

解决此问题
use open qw/ :std :encoding(utf8) /;

指定要应用于新打开的输入(和输出)句柄的默认图层,包括ARGV。因此,当第一次执行<>时自动打开它时,您的数据应该正确读取。


<强>更新

我还明白了输出文本错误的原因。

我错误的想法是,即使将输入读作八位字节序列而不是UTF-8编码的宽字符,如果将相同的八位字节未经修改地复制到输出中,它仍应产生正确的结果。

现在非常明显的是,虽然输入以字节为单位,但STDOUT设置为UTF-8编码,因此已经编码的数据将被重新编码。让我们从上面的 lamb 中取出这个希伯来语单词

[D7 9B] [D6 BC] [D6 B6] [D7 91] [D6 B6] [D7 A9] [D7 82]

由于ARGV仍设置为:raw,因此输入被解释为这14个单字节字符而不是7个UTF-8编码的宽字符

D7 9B D6 BC D6 B6 D7 91 D6 B6 D7 A9 D7 82

现在,如果打印出该字符串,那么它将被编码为UTF-8,因为这是STDOUT的设置方式。 ASCII(7位)字符将保持UTF-8编码不受影响,但此字符串中的所有“字符”都在代码点0x80或更高,因此它们将被编码为多字节字符。

对这十四个“字符”进行编码的结果是这一系列的二十八个八位字节

[C3 97] [C2 9B] [C3 96] [C2 BC] [C3 96] [C2 B6] [C3 97] [C2 91] [C3 96] [C2 B6] [C3 97] [C2 A9] [C3 97] [C2 82]

,当显示为UTF8编码的字符串时,将显示为14个无意义的“字符”,这些字符是从ARGV读取而不解码的结果。

呃,我想QED。