我有一些带有大量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;毫无智慧。也许我需要设置一些环境变量?或者该脚本现在已弃用?还是...?
答案 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
读取而不解码的结果。