某些波斯语文本使用宽字符打印,但其他文本则不然
Wide charectar in print for some Farsi text, but not others
我正在使用 Google Translate 通过 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 '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 输出中的 \u200c
是一个 "Zero Width Non-Joiner" unicode 字符。当 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,它将修复 \u200c
。感谢@Shawn 为我指明了正确的方向:
my $j = JSON->new;
$j->utf8(1);
my $json = $j->decode($c);
现在,在返回 JSON 散列时,JSON-formatted 文本内容如 \u200c
被正确音译为 \xe2\x80\x8c
。
这里发生了很多事情。我认为很多,尤其是在前两个示例中,源于不理解 perl 的两种字符串模式(面向字节和面向 Unicode 代码点)之间的区别。
示例 1 是一个原始字节字符串,其中包含恰好采用 UTF-8 编码的字节,并且未更改地通过;只要显示输出的终端需要 UTF-8,它们就会被正确呈现。示例 2 有一个 'wide' 字符(值大于 255),使其成为 Unicode 字符串,其中每个由大于 127 的 \x{NN}
数字表示的字符是一个编码为多个字节的 Unicode 代码点在 UTF-8 中。打印它会导致 mojibake 和警告,因为标准输出是面向字节的,没有转换层。
正如我在评论中建议的那样,阅读 perluniintro
(以及其他 unicode-related 文档)是了解事物运作方式的良好开端。
但在实际任务中,从 curl
命令返回的 JSON 中提取文本...如果这是 [=],我会使用 jq
44=] 脚本:
$ curl ... | jq -r '.[0][0][0]'
افزودن یک کارت پیشفرض 111
与等效的 perl 比较 one-liner:
$ curl ... | perl -CS -MJSON -lne 'print from_json($_)->[0][0][0]'
افزودن یک کارت پیشفرض 111
-CS
参数告诉 perl 标准输入、输出和错误都是 UTF-8 编码的。您也可以使用 -CO
来制作标准输出,并使用 decode_json()
代替,它需要原始 UTF-8 编码字节而不是 Unicode 字符串。
并且在脚本而不是 one-liner 中,使用面向 JSON
的 OO 接口并使用其方法调整输入字符串的编码方式,加上 open
编译指示(或binmode
或 open
的编码层)而不是 -C
选项,是要走的路。
我正在使用 Google Translate 通过 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 '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 输出中的 \u200c
是一个 "Zero Width Non-Joiner" unicode 字符。当 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,它将修复 \u200c
。感谢@Shawn 为我指明了正确的方向:
my $j = JSON->new;
$j->utf8(1);
my $json = $j->decode($c);
现在,在返回 JSON 散列时,JSON-formatted 文本内容如 \u200c
被正确音译为 \xe2\x80\x8c
。
这里发生了很多事情。我认为很多,尤其是在前两个示例中,源于不理解 perl 的两种字符串模式(面向字节和面向 Unicode 代码点)之间的区别。
示例 1 是一个原始字节字符串,其中包含恰好采用 UTF-8 编码的字节,并且未更改地通过;只要显示输出的终端需要 UTF-8,它们就会被正确呈现。示例 2 有一个 'wide' 字符(值大于 255),使其成为 Unicode 字符串,其中每个由大于 127 的 \x{NN}
数字表示的字符是一个编码为多个字节的 Unicode 代码点在 UTF-8 中。打印它会导致 mojibake 和警告,因为标准输出是面向字节的,没有转换层。
正如我在评论中建议的那样,阅读 perluniintro
(以及其他 unicode-related 文档)是了解事物运作方式的良好开端。
但在实际任务中,从 curl
命令返回的 JSON 中提取文本...如果这是 [=],我会使用 jq
44=] 脚本:
$ curl ... | jq -r '.[0][0][0]'
افزودن یک کارت پیشفرض 111
与等效的 perl 比较 one-liner:
$ curl ... | perl -CS -MJSON -lne 'print from_json($_)->[0][0][0]'
افزودن یک کارت پیشفرض 111
-CS
参数告诉 perl 标准输入、输出和错误都是 UTF-8 编码的。您也可以使用 -CO
来制作标准输出,并使用 decode_json()
代替,它需要原始 UTF-8 编码字节而不是 Unicode 字符串。
并且在脚本而不是 one-liner 中,使用面向 JSON
的 OO 接口并使用其方法调整输入字符串的编码方式,加上 open
编译指示(或binmode
或 open
的编码层)而不是 -C
选项,是要走的路。