我有一些包含大量 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/