2011-06-30 2 views
3

JSONライブラリで処理するとUTF-8文字が破棄されます(これはProblem with decoding unicode JSON in perlと似ていますが、binmodeを設定すると別の問題が発生します)。perlのUTF-8 JSONを解読する際の問題

は、私は、次の例にまで問題が低下している:

(hlovdal) localhost:/tmp/my_test>cat my_test.pl 
#!/usr/bin/perl -w 
use strict; 
use warnings; 
use JSON; 
use File::Slurp; 
use Getopt::Long; 
use Encode; 

my $set_binmode = 0; 
GetOptions("set-binmode" => \$set_binmode); 

if ($set_binmode) { 
     binmode(STDIN, ":encoding(UTF-8)"); 
     binmode(STDOUT, ":encoding(UTF-8)"); 
     binmode(STDERR, ":encoding(UTF-8)"); 
} 

sub check { 
     my $text = shift; 
     return "is_utf8(): " . (Encode::is_utf8($text) ? "1" : "0") . ", is_utf8(1): " . (Encode::is_utf8($text, 1) ? "1" : "0"). ". "; 
} 

my $my_test = "hei på deg"; 
my $json_text = read_file('my_test.json'); 
my $hash_ref = JSON->new->utf8->decode($json_text); 

print check($my_test), "\$my_test = $my_test\n"; 
print check($json_text), "\$json_text = $json_text"; 
print check($$hash_ref{'my_test'}), "\$\$hash_ref{'my_test'} = " . $$hash_ref{'my_test'} . "\n"; 

(hlovdal) localhost:/tmp/my_test> 

テキストをテスト実行しているISO-8859-1にcrippeled何らかの理由です。 binmode sort ofを設定すると、それを解決しますが、他の文字列を二重にエンコードします。

(hlovdal) localhost:/tmp/my_test>cat my_test.json 
{ "my_test" : "hei på deg" } 
(hlovdal) localhost:/tmp/my_test>file my_test.json 
my_test.json: UTF-8 Unicode text 
(hlovdal) localhost:/tmp/my_test>hexdump -c my_test.json 
0000000 {  " m y _ t e s t "  :  " h 
0000010 e i  p 303 245  d e g "  } \n   
000001e 
(hlovdal) localhost:/tmp/my_test> 
(hlovdal) localhost:/tmp/my_test>perl my_test.pl 
is_utf8(): 0, is_utf8(1): 0. $my_test = hei på deg 
is_utf8(): 0, is_utf8(1): 0. $json_text = { "my_test" : "hei på deg" } 
is_utf8(): 1, is_utf8(1): 1. $$hash_ref{'my_test'} = hei p� deg 
(hlovdal) localhost:/tmp/my_test>perl my_test.pl --set-binmode 
is_utf8(): 0, is_utf8(1): 0. $my_test = hei på deg 
is_utf8(): 0, is_utf8(1): 0. $json_text = { "my_test" : "hei på deg" } 
is_utf8(): 1, is_utf8(1): 1. $$hash_ref{'my_test'} = hei på deg 
(hlovdal) localhost:/tmp/my_test> 

この原因と解決方法は何ですか?


これは、新しくインストールされた最新のFedora 15システムにあります。

(hlovdal) localhost:/tmp/my_test>perl --version | grep version 
This is perl 5, version 12, subversion 4 (v5.12.4) built for x86_64-linux-thread-multi 
(hlovdal) localhost:/tmp/my_test>rpm -q perl-JSON 
perl-JSON-2.51-1.fc15.noarch 
(hlovdal) localhost:/tmp/my_test>locale 
LANG=en_US.UTF-8 
LC_CTYPE="en_US.UTF-8" 
LC_NUMERIC="en_US.UTF-8" 
LC_TIME="en_US.UTF-8" 
LC_COLLATE="en_US.UTF-8" 
LC_MONETARY="en_US.UTF-8" 
LC_MESSAGES="en_US.UTF-8" 
LC_PAPER="en_US.UTF-8" 
LC_NAME="en_US.UTF-8" 
LC_ADDRESS="en_US.UTF-8" 
LC_TELEPHONE="en_US.UTF-8" 
LC_MEASUREMENT="en_US.UTF-8" 
LC_IDENTIFICATION="en_US.UTF-8" 
LC_ALL= 
(hlovdal) localhost:/tmp/my_test> 

更新:use utf8を追加する文字がまだ(以前は若干異なるが)右に処理されていない、それを解決しない。

(hlovdal) localhost:/tmp/my_test>perl my_test.pl 
is_utf8(): 1, is_utf8(1): 1. $my_test = hei p� deg 
is_utf8(): 0, is_utf8(1): 0. $json_text = { "my_test" : "hei på deg" } 
is_utf8(): 1, is_utf8(1): 1. $$hash_ref{'my_test'} = hei p� deg 
(hlovdal) localhost:/tmp/my_test>perl my_test.pl --set-binmode 
is_utf8(): 1, is_utf8(1): 1. $my_test = hei på deg 
is_utf8(): 0, is_utf8(1): 0. $json_text = { "my_test" : "hei på deg" } 
is_utf8(): 1, is_utf8(1): 1. $$hash_ref{'my_test'} = hei på deg 
(hlovdal) localhost:/tmp/my_test> 

perlunifaq

で表されているようPerlソースでUnicodeを使用できますか?

はい、できます。ソースが UTF-8でエンコードされている場合は、utf8プラグマを使用して と指定できます。

use utf8; 

これは、お客様の 入力、または出力には何も行いません。 は、あなたのソースが の方法に影響します。文字列 リテラルのUnicodeを識別子で使用することができます(ただし、 は\ wによれば "word characters" でなければなりません)。また、カスタムの 区切り文字でも使用できます。

答えて

-3

問題の核心は、JSONのオクテット配列の期待の代わりに(this questionに溶解)文字列でした。しかし、私はまた、 "utf8を使う"のような、ユニコードに関連するいくつかのものを欠いていました。

--- my_test.pl.orig 2011-08-03 15:44:44.217868886 +0200 
+++ my_test.pl 2011-08-03 15:55:30.152379269 +0200 
@@ -1,19 +1,14 @@ 
-#!/usr/bin/perl -w 
+#!/usr/bin/perl -CSAD 
use strict; 
use warnings; 
use JSON; 
use File::Slurp; 
use Getopt::Long; 
use Encode; 
- 
-my $set_binmode = 0; 
-GetOptions("set-binmode" => \$set_binmode); 
- 
-if ($set_binmode) { 
-  binmode(STDIN, ":encoding(UTF-8)"); 
-  binmode(STDOUT, ":encoding(UTF-8)"); 
-  binmode(STDERR, ":encoding(UTF-8)"); 
-} 
+use utf8; 
+use warnings qw< FATAL utf8 >; 
+use open qw(:encoding(UTF-8) :std); 
+use feature qw<unicode_strings>; 

sub check { 
     my $text = shift; 
@@ -21,8 +16,9 @@ 
} 

my $my_test = "hei på deg"; 
-my $json_text = read_file('my_test.json'); 
-my $hash_ref = JSON->new->utf8->decode($json_text); 
+my $json_text = read_file('my_test.json', binmode => ':encoding(UTF-8)'); 
+my $json_bytes = encode('UTF-8', $json_text); 
+my $hash_ref = JSON->new->utf8->decode($json_bytes); 

print check($my_test), "\$my_test = $my_test\n"; 
print check($json_text), "\$json_text = $json_text"; 
+0

なぜ-1が投票されましたか? – hlovdal

8

あなたのプログラムはUTF-8で保存しましたが、Perlには忘れてしまいました。 use utf8;を追加します。

また、あまりにも複雑すぎるプログラミングです。 JSONはDWYMを機能させます。ものを調べるには、Devel :: Peekを使います。

use utf8; # for the following line 
my $my_test = 'hei på deg'; 

use Devel::Peek qw(Dump); 
use File::Slurp (read_file); 
use JSON qw(decode_json); 

my $hash_ref = decode_json(read_file('my_test.json')); 

Dump $hash_ref; # Perl character strings 
Dump $my_test; # Perl character string 
+0

ご回答ありがとうございますが、私の質問には答えられません。プログラムの終わりに '$$ hash_ref {'my_test'}、" \ n "、print $ my_test、" \ n ";を追加するとiso-8859-1形式で2回印刷されます。 perlunifaqが指摘したように、 'use utf8'はI/Oではなくソースコードにのみ影響します。 – hlovdal

+3

Perl文字列をSTDOUTに出力するのは間違いです。最初にオクテットにエンコードする必要があります。明示的に 'use Encode qw(encode); 'UTF-8'、$ my_test;または暗黙的に 'binmode STDOUT ':エンコーディング(UTF-8)'; print $ my_test; 'また、これはISO-8859-1ではありませんが、Perlの内部エンコーディングは歴史的な理由からひどく複雑で、あなたが言ったように見えるだけです*。エンコーディングのトピックを厳密に紹介する必要があります。http://p3rl.org/UNI – daxim

+0

daximによるこの回答は、シンプルさの点では完璧です(私は思う)。ありがとうございました! – Wick

0

はそれだけで私の印象です、またはこのperlのライブラリはあなたがisoLatin1文字列(UTF-8にUTF-8バイトのコードを書くことを期待しない:ここでは例のコードは完全に動作させるために必要なdiffがあります文字列でフラグが無効になっています)。 そして同様に、それはISOラテン文字列であなたUTF-8のバイトコードに返します。

#! /usr/bin/perl -w 
use strict; 
use Encode; 
use Data::Dumper qw(Dumper); 
use JSON; # imports encode_json, decode_json, to_json and from_json. 
use utf8; 

############### 
## EXAMPLE 1: 
################ 
my $json = JSON->new->allow_nonref; 
my $exampleAJsonObj = { key1 => 'a'}; 
my $exampleAText = $json->utf8->encode($exampleAJsonObj); 
my $exampleAJsonObfUtf = { key1 => 'ä'}; 
my $exampleATextUtf = $json->utf8->encode($exampleAJsonObfUtf); 


#binmode(STDOUT, ":utf8"); 
print "EXAMPLE1: "; 
print "\n"; 
print encode 'UTF-8', "exampleAText: $exampleAText and as object: " . Dumper($exampleAJsonObj); 
print "\n"; 
print encode 'UTF-8', "exampleATextUtf: $exampleATextUtf and as object: " . Dumper($exampleAJsonObfUtf) . " Key1 was: " . $exampleAJsonObfUtf->{key1}; 
print "\n"; 
print hexdump($exampleAText); 
print "\n"; 
print hexdump($exampleATextUtf); 
print "\n"; 

############################# 
## SUB. 
############################# 
# For a given string parameter, returns a string which shows 
# whether the utf8 flag is enabled and a byte-by-byte view 
# of the internal representation. 
# 
sub hexdump 
{ 
    my $str = shift; 
    my $flag = Encode::is_utf8($str) ? 1 : 0; 
    use bytes; # this tells unpack to deal with raw bytes 
    my @internal_rep_bytes = unpack('C*', $str); 
    return 
     $flag 
     . '(' 
     . join(' ', map { sprintf("%02x", $_) } @internal_rep_bytes) 
     . ')'; 
} 

最後に、出力は次のようになります。だから、

exampleAText: {"key1":"a"} and as object: $VAR1 = { 
      'key1' => 'a' 
     }; 

exampleATextUtf: {"key1":"ä"} and as object: $VAR1 = { 
      'key1' => "\x{e4}" 
     }; 
Key1 was: ä 
0(7b 22 6b 65 79 31 22 3a 22 61 22 7d) 
0(7b 22 6b 65 79 31 22 3a 22 c3 a4 22 7d) 

、我々は、このプロセスの最後であることを確認outpu文字列のどちらもUTF-8文字列ではありません。これはfalseです。少なくとも、0(7b 22 6b 65 79 31 22 3a 22 c3 a4 22 7d)。 A4をC3 お知らせä http://www.utf8-chartable.de/

の正しいバイトコードは、したがって、ライブラリは1つが非UTF-8文字列にUTF-8バイトコードを鉱脈するために期待しているようだし、その結果、それが行います同じこと、UTF-8バイトコードでNON utf-8文字列を出力します。

私は間違っていますか?

さらなる実験で、結論に至りました。 返され消費されたperlObjectsには、(私が予想したように)文字列にUTF-8としてフラグが立てられています。 decode/encodeから消費されて返されるperl文字列は、perlにはISO latin1文字列として表示されなければなりませんが、utf8バイトコードを持ちます。 UTF8 jsonを含むファイルを開くときは、 "<:encoding(UTF-8)"を使用しないでください。

関連する問題