DATA 和 ARGV 文件句柄上的 Perl UTF-8 编码

标签 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),但我还是一头雾水。也许我需要设置一些环境变量?或者现在不赞成使用该脚本中的某些内容?或者……?

最佳答案

你的输出没问题。 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 上设置一个 UTF-8 Perl IO 层与 binmode ,正如您所做的那样,没有任何效果。此外,您无法设置 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 ,输入被解释为这十四个单字节字符,而不是七个 UTF-8 编码的宽字符

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

现在,如果该字符串被打印,那么它将被编码为 UTF-8,因为这就是 STDOUT 的方式。已经设置好了。 ASCII(七位)字符将在 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 编码的字符串时,将显示为从 ARGV 读取的结果的十四个无意义“字符”无需解码。

嗯,我想是 QED。

关于DATA 和 ARGV 文件句柄上的 Perl UTF-8 编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25473122/

相关文章:

linux - Linux bash 脚本中的 Perl 脚本

javascript - jQuery 元素未传递给 perl cgi

python - 如何正确地将unicode字符写入文件

java - 将 unicode 字符串发送到服务器套接字

mysql - 如何一次性修改mariadb图表集?

c - 使用 iconv() 的 UTF-8 到 C/POSIX 语言环境转换失败

python-3.x - 无法预测表情符号的情绪

perl - Perl 中引用的目的是什么?

perl - 如何根据模块版本有条件地更改@INC?

unicode - 在Rust中将Unicode字符串转换为NFC