提问人:KJ7LNW 提问时间:4/9/2022 更新时间:4/9/2022 访问量:78
一些波斯语文本的印刷品很宽,但其他文本则不然
Wide charectar in print for some Farsi text, but not others
问:
我正在使用谷歌翻译将一些错误代码转换为带有 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 字符。当解析时,它会爆炸:\u200c
JSON::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
答:
JSON 对象需要启用 utf8,它将修复 .感谢@Shawn为我指明了正确的方向:\u200c
my $j = JSON->new;
$j->utf8(1);
my $json = $j->decode($c);
现在,在返回 JSON 哈希时,JSON 格式的文本内容 like 已正确音译。\u200c
\xe2\x80\x8c
这里发生了很多事情。我认为其中很多,尤其是在前两个例子中,源于不理解 perl 的两种字符串模式(面向字节和 Unicode 代码点)之间的区别。
示例 1 是一个原始字节字符串,其中包含恰好是 UTF-8 编码的字节,并且原封不动地传递;只要显示输出的终端需要 UTF-8,它们就会被正确呈现。示例 2 具有一个“宽”字符(值大于 255),使其成为 Unicode 字符串,其中由大于 127 的数字表示的每个字符都是一个 Unicode 代码点,以 UTF-8 编码为多个字节。打印这会导致 mojibake 和警告,因为标准输出是面向字节的,没有转换层。\x{NN}
正如我在评论中建议的那样,阅读(以及其他与 unicode 相关的文档)是了解事物如何工作的良好开端。perluniintro
但是在实际任务中,从命令返回的 JSON 中提取文本......如果这是用于 shell 脚本,我会改用:curl
jq
$ 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
-CO
decode_json()
在脚本而不是单行脚本中,使用 OO 接口来调整输入字符串的编码方式,并使用其方法加上编译指示(或编码层)而不是选项,是要走的路。JSON
open
binmode
open
-C
评论
jq
from_json
评论
\x{200c}
\xE2\x80\x8C
)-CO
\x{200c} with \xE2\x80\x8C