【问题标题】:Perl's JSON::XS not encoding UTF8 correctly?Perl 的 JSON::XS 没有正确编码 UTF8?
【发布时间】:2015-09-19 03:37:31
【问题描述】:

这个简单的代码段显示了我在 Perl 中使用 JSON::XS 编码时遇到的问题:

#!/usr/bin/perl
use strict;
use warnings;
use JSON::XS; 
use utf8;
binmode STDOUT, ":encoding(utf8)";

my (%data);

$data{code} = "Gewürztraminer";
print "data{code} = " . $data{code} . "\n";

my $json_text = encode_json \%data;
print $json_text . "\n";

这产生的输出是:

johnnyb@boogie:~/Projects/repos > ./jsontest.pl 
data{code} = Gewürztraminer
{"code":"Gewürztraminer"}

现在,如果我注释掉上面的 binmode 行,我会得到:

johnnyb@boogie:~/Projects/repos > ./jsontest.pl 
data{code} = Gew�rztraminer
{"code":"Gewürztraminer"}

这里发生了什么?请注意,我试图在无法使用 binmode 的 perl CGI 脚本中修复此行为,但我总是得到 JSON 流中返回的上述“ü”字符。我该如何调试?我错过了什么?

【问题讨论】:

  • 将最后一行替换为print decode('UTF-8', $json_text, Encode::FB_CROAK) . "\n";

标签: json perl cgi


【解决方案1】:

encode_jsonJSON::XS->new->utf8->encode 的缩写)使用 UTF-8 进行编码,然后您通过将其打印到添加了编码层的 STDOUT 来对其进行重新编码。实际上,你正在做encode_utf8(encode_utf8($uncoded_json))

解决方案 1

use open ':std', ':encoding(utf8)';  # Defaults
binmode STDOUT;                      # Override defaults
print encode_json(\%data);

解决方案 2

use open ':std', ':encoding(utf8)';    # Defaults
print JSON::XS->new->encode(\%data);   # Or to_json from JSON.pm

解决方案 3

通过对非 ASCII 使用 \u 转义符,以下内容适用于 STDOUT 上的任何编码:

print JSON::XS->new->ascii->encode(\%data);

在 cmets 中,您提到它实际上是一个 CGI 脚本。

#!/usr/bin/perl
use strict;
use warnings;

use utf8;                      # Encoding of source code.
use open ':encoding(UTF-8)';   # Default encoding of file handles.
BEGIN {
   binmode STDIN;                       # Usually does nothing on non-Windows.
   binmode STDOUT;                      # Usually does nothing on non-Windows.
   binmode STDERR, ':encoding(UTF-8)';  # For text sent to the log file.
}

use CGI      qw( -utf8 );
use JSON::XS qw( ); 

{
   my $cgi = CGI->new();
   my $data = { code => "Gewürztraminer" };
   print $cgi->header('application/json');
   print encode_json($data);
}

【讨论】:

  • 是的 - 这确实有效,如下所述,我认为我正在编码已经是 UTF8 的数据。不知道如何解决这个问题。不幸的是,STDOUT 在 CGI 脚本中确实没有意义(?)所以我不确定我是否可以使用上面的方法。
  • 你没有任何意义。如果您不使用 STDOUT,您将不会遇到问题。是的,CGI 确实使用 STDOUT。
  • 我无法想出将 CGI 对象用作文件处理来交给 binmode。编码后的 json 数据必须打印到 CGI 对象,然后返回给发出 AJAX 请求的 jQuery。如果您知道如何做到这一点,我会全力以赴。
  • 你在说什么。我们已经确定 CGI 使用 STDOUT,因此您可以使用我发布的代码。
  • @Omortis CGI.pm 不会在任何地方发送任何内容。你的代码需要print 的东西。 CGI.pm 只是为您提供了为您生成 HTML 的蹩脚的旧函数。还要记住,CGI.pm 不再是核心。 :)
【解决方案2】:

JSON::XS 将其输出编码为八位字节。它表示编码的utf8字符串的外部表示,但它不是unicode字符串。有关详细信息,请参阅perlunicode。简而言之,$json_text 的内容是由IO 处理程序以二进制代码准备传输的。如果您在use utf8; 之后创建$data{code} 的标量内容,您将拥有包含内部编码的Unicode 字符串的标量。 (内部编码为 utf8,但它是您不应该依赖的实现细节。Pragma use utf8; 表示源代码被编码为 utf8,仅此而已。)如果您想以 utf8 编码输出两个标量IO 处理程序,您必须将 $json_string 转换为内部 unicode 字符字符串。

use strict;
use warnings;
use JSON::XS; 
use utf8;
binmode STDOUT, ":encoding(utf8)";

my (%data);

$data{code} = "Gewürztraminer";
print "data{code} = " . $data{code} . "\n";

my $json_text = encode_json \%data;
utf8::decode($json_text);
print $json_text . "\n";

或者它打算如何使用,使用二进制模式的IO处理程序输出编码字符串。

my $json_text = encode_json \%data;
binmode STDOUT;
print $json_text . "\n";

试试

print utf8::is_utf8($json_text) ? "UTF8" : "OCTETS" . "\n";

看看里面有什么。

【讨论】:

  • 要求 UTF-8 编码只是为了跟进一个解码和另一个编码,这是相当浪费的!
  • 是的,但重点是解释哪里出了问题,而不是让它变得高效。
  • 代码没有解释,解释有。
  • 永远不要使用is_utf8。如果需要强制使用两种存储格式之一,使用utf8::upgradeutf8::downgrade,这里都不需要。
  • @Omortis:你从 DBI 读取数据?开始了。检查从 DBI 读取的数据是否在内部使用 is_utf8 标记为 Unicode,如果没有,则使用 utf8::upgrade。这是 DBI 驱动程序中的常见错误。然后使用@ikegami 的答案中的方法并选择您是否希望 IO 采用二进制或 utf8 模式。
猜你喜欢
  • 2016-04-08
  • 2016-05-31
  • 1970-01-01
  • 1970-01-01
  • 2016-01-12
  • 2012-10-20
  • 2012-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多