一些波斯语文本的印刷品很宽,但其他文本则不然

Wide charectar in print for some Farsi text, but not others

提问人:KJ7LNW 提问时间:4/9/2022 更新时间:4/9/2022 访问量:78

问:

我正在使用谷歌翻译将一些错误代码转换为带有 Perl 的波斯语。波斯语就是这样一个例子,我在其他语言中也发现了这个问题---但在这次讨论中,我将坚持使用单个示例:

“几何数据卡错误”的翻译文本工作正常(示例 1),但翻译“附加默认 111 卡”(示例 2)会产生“宽字符”错误。

这两个示例都可以从终端运行,它们只是打印。

我尝试过这些通常的事情,但无济于事:

use utf8;
use open ':std', ':encoding(UTF-8)';
binmode STDOUT, ':encoding(UTF-8)';

示例 1:这有效

perl -Mutf8 -le 'print "\x{d8}\x{ae}\x{d8}\x{b7}\x{d8}\x{a7}\x{db}\x{8c} \x{da}\x{a9}\x{d8}\x{a7}\x{d8}\x{b1}\x{d8}\x{aa} \x{d8}\x{af}\x{d8}\x{a7}\x{d8}\x{af}\x{d9}\x{87} \x{d9}\x{87}\x{d9}\x{86}\x{d8}\x{af}\x{d8}\x{b3}\x{db}\x{8c}"'
خطای کارت داده هندسی

示例 2:这会产生宽字符警告并打印噪音

perl -Mutf8 -le 'print "\x{d8}\x{a7}\x{d9}\x{81}\x{d8}\x{b2}\x{d9}\x{88}\x{d8}\x{af}\x{d9}\x{86} \x{db}\x{8c}\x{da}\x{a9} \x{da}\x{a9}\x{d8}\x{a7}\x{d8}\x{b1}\x{d8}\x{aa} \x{d9}\x{be}\x{db}\x{8c}\x{d8}\x{b4}\x{200c}\x{d9}\x{81}\x{d8}\x{b1}\x{d8}\x{b6} 111"'
Wide character in print at -e line 1.
# <terminal noise, not Farsi text>

使用 Curl

如果我执行相同的请求,我会得到这个:curl

curl 'https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=fa&hl=fa&dt=t&ie=UTF-8&oe=UTF-8&otf=1&ssel=0&tsel=0&tk=xxxx&dt=dj&q=%41%70%70%65%6E%64%69%6E%67%20%61%20%64%65%66%61%75%6C%74%20%31%31%31%20%63%61%72%64'
[[["افزودن یک کارت پیش\u200cفرض 111","Appending a default 111 card",null,null,3,null,null,[[]],[[["982c75c78c6c8e6005ec3a4021a7f785","tea_GrecoIndoEuropeA_en2elfahykakumksq_2021q3.md"]]]]],null,"en",null,null,null,1,[],[["en"],null,[1],["en"]]]

请注意,在上面的 JSON 输出中,它是一个“零宽度非连接器”unicode 字符。当解析时,它会爆炸:\u200cJSON::from_json\u200c

perl -Mutf8 -MJSON -e 'print from_json("[\"\\u200c\"]")->[0];'
Wide character in print at -e line 1.

我可以像这样“修复”它:

my $c = $res->content;
$c =~ s/\\u[0-9a-f]{4}//;
my $json = from_json($c);

然后输出文本是正确的(从右到左):

افزودن یک کارت پیشفرض 111

问:这是怎么回事?

  • 这是Perl中的错误还是JSON中的错误?
  • 应该以其他方式正确解析吗?\u200c
Perl Unicode UTF-8 翻译 widechar

评论

0赞 Shawn 4/9/2022
您的第一个示例看起来像一堆转义的 utf-8 字节,而不是实际的 utf-8 编码文本。第二个示例将其与转义的 Unicode 字符混合。应坚持使用一种或另一种样式(替换为\x{200c}\xE2\x80\x8C)
0赞 Shawn 4/9/2022
该选项将告诉 perl 将写入 stdout 的 Unicode 文本编码为 utf-8,并禁止在单行代码中显示警告。有关详细信息,请参见 perlrun。-CO
1赞 Shawn 4/9/2022
我不认为您的任何代码片段都需要 utf8 模块;这只是告诉 perl 脚本是用 UTF-8 编码的,而你的所有脚本看起来都像普通的 ASCII。
0赞 Shawn 4/9/2022
阅读 perldoc.perl.org/perluniintro 是个好主意;它更详细地介绍了其中的一些内容。具体而言,请参阅 perldoc.perl.org/perluniintro#Perl's-Unicode-Model 的第二个示例。
0赞 KJ7LNW 4/9/2022
@Shawn,更换确实可以解决它!有没有一种 perl 方法可以以编程方式替换它?我会阅读你引用的 perl 文档,看看我想出了什么,但如果你知道一个快速修复......\x{200c} with \xE2\x80\x8C

答:

1赞 KJ7LNW 4/9/2022 #1

JSON 对象需要启用 utf8,它将修复 .感谢@Shawn为我指明了正确的方向:\u200c

my $j = JSON->new;
$j->utf8(1);
my $json = $j->decode($c);

现在,在返回 JSON 哈希时,JSON 格式的文本内容 like 已正确音译。\u200c\xe2\x80\x8c

3赞 Shawn 4/9/2022 #2

这里发生了很多事情。我认为其中很多,尤其是在前两个例子中,源于不理解 perl 的两种字符串模式(面向字节和 Unicode 代码点)之间的区别。

示例 1 是一个原始字节字符串,其中包含恰好是 UTF-8 编码的字节,并且原封不动地传递;只要显示输出的终端需要 UTF-8,它们就会被正确呈现。示例 2 具有一个“宽”字符(值大于 255),使其成为 Unicode 字符串,其中由大于 127 的数字表示的每个字符都是一个 Unicode 代码点,以 UTF-8 编码为多个字节。打印这会导致 mojibake 和警告,因为标准输出是面向字节的,没有转换层。\x{NN}

正如我在评论中建议的那样,阅读(以及其他与 unicode 相关的文档)是了解事物如何工作的良好开端。perluniintro


但是在实际任务中,从命令返回的 JSON 中提取文本......如果这是用于 shell 脚本,我会改用:curljq

$ curl ... | jq -r '.[0][0][0]'
افزودن یک کارت پیش‌فرض 111

与等效的 perl 单行代码相比:

$ curl ... | perl -CS -MJSON -lne 'print from_json($_)->[0][0][0]'
افزودن یک کارت پیش‌فرض 111

该参数告诉 perl 标准输入、输出和错误都是 UTF-8 编码的。您也可以使用仅标准输出,并改用,它需要原始 UTF-8 编码的字节而不是 Unicode 字符串。-CS-COdecode_json()

在脚本而不是单行脚本中,使用 OO 接口来调整输入字符串的编码方式,并使用其方法加上编译指示(或编码层)而不是选项,是要走的路。JSONopenbinmodeopen-C

评论

0赞 KJ7LNW 4/9/2022
仔细观察,您可以看到 ش ف 与 شف 之间缺少的 ZWJN 在波斯语脚本左侧约 3 个字形的 vs 版本上。-CS 可能会抑制宽字符警告,但from_json丢失了 ZWJN (\u200c)。但是,正如您所建议的那样,从启用了 ->utf8() 的 JSON 对象运行 from_json 将修复它。jqfrom_json
0赞 Shawn 4/9/2022
@KJ7LNW当我通过十六进制转储运行它时,我在实际输出中看到了它。(由于复制和粘贴和/或SO的东西,可能在这个答案中丢失了。