2011-08-01 13 views
11

this questionおよびthis answer(他の質問への)に関連して、私はまだJSONでUTF-8を処理できません。perl:JSON文字列の不正な形式のUTF-8文字

私は、最高のエキスパートからの推薦に基づいて、必要なすべてのブードーが呼び出されていることを確認しましたが、文字列が有効であり、可能な限りUTF-8としてマークされ、ラベル付けされていることがわかります。しかし、まだのperl

Uncaught exception: malformed UTF-8 character in JSON string 

または

Uncaught exception: Wide character in subroutine entry 

のいずれかで死ぬ私はここで間違って何をしているのですか?これを実行する

(hlovdal) localhost:/work/2011/perl_unicode>cat json_malformed_utf8.pl 
#!/usr/bin/perl -w -CSAD 

### BEGIN ### 
# Apparently the very best perl unicode boiler template code that exist, 
# https://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/6163129#6163129 
# Slightly modified. 

use v5.12; # minimal for unicode string feature 
#use v5.14; # optimal for unicode string feature 

use utf8;             # Declare that this source unit is encoded as UTF‑8. Although 
                  # once upon a time this pragma did other things, it now serves 
                  # this one singular purpose alone and no other. 
use strict; 
use autodie; 

use warnings;            # Enable warnings, since the previous declaration only enables 
use warnings qw< FATAL utf8  >;     # strictures and features, not warnings. I also suggest 
                  # promoting Unicode warnings into exceptions, so use both 
                  # these lines, not just one of them. 

use open  qw(:encoding(UTF-8) :std);    # Declare that anything that opens a filehandles within this 
                  # lexical scope but not elsewhere is to assume that that 
                  # stream is encoded in UTF‑8 unless you tell it otherwise. 
                  # That way you do not affect other module’s or other program’s code. 

use charnames qw<:full>;        # Enable named characters via \N{CHARNAME}. 
use feature  qw<unicode_strings>; 

use Carp    qw< carp croak confess cluck >; 
use Encode    qw< encode decode >; 
use Unicode::Normalize qw< NFD NFC >; 

END { close STDOUT } 

if (grep /\P{ASCII}/ => @ARGV) { 
    @ARGV = map { decode("UTF-8", $_) } @ARGV; 
} 

$| = 1; 

binmode(DATA, ":encoding(UTF-8)");      # If you have a DATA handle, you must explicitly set its encoding. 

# give a full stack dump on any untrapped exceptions 
local $SIG{__DIE__} = sub { 
    confess "Uncaught exception: @_" unless $^S; 
}; 

# now promote run-time warnings into stackdumped exceptions 
# *unless* we're in an try block, in which 
# case just generate a clucking stackdump instead 
local $SIG{__WARN__} = sub { 
    if ($^S) { cluck "Trapped warning: @_" } 
    else  { confess "Deadly warning: @_" } 
}; 

### END ### 


use JSON; 
use Encode; 

use Getopt::Long; 
use Encode; 

my $use_nfd = 0; 
my $use_water = 0; 
GetOptions("nfd" => \$use_nfd, "water" => \$use_water); 

print "JSON->backend->is_pp = ", JSON->backend->is_pp, ", JSON->backend->is_xs = ", JSON->backend->is_xs, "\n"; 

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 $json_text = "{ \"my_test\" : \"hei på deg\" }\n"; 
if ($use_water) { 
     $json_text = "{ \"water\" : \"水\" }\n"; 
} 
if ($use_nfd) { 
     $json_text = NFD($json_text); 
} 

print check($json_text), "\$json_text = $json_text"; 

# test from perluniintro(1) 
if (eval { decode_utf8($json_text, Encode::FB_CROAK); 1 }) { 
     print "string is valid utf8\n"; 
} else { 
     print "string is not valid utf8\n"; 
} 

my $hash_ref1 = JSON->new->utf8->decode($json_text); 
my $hash_ref2 = decode_json($json_text); 

__END__ 

(hlovdal) localhost:/work/2011/perl_unicode>./json_malformed_utf8.pl 
JSON->backend->is_pp = 0, JSON->backend->is_xs = 1 
is_utf8(): 1, is_utf8(1): 1. $json_text = { "my_test" : "hei på deg" } 
string is valid utf8 
Uncaught exception: malformed UTF-8 character in JSON string, at character offset 20 (before "\x{5824}eg" }\n") at ./json_malformed_utf8.pl line 96. 
at ./json_malformed_utf8.pl line 46 
     main::__ANON__('malformed UTF-8 character in JSON string, at character offset...') called at ./json_malformed_utf8.pl line 96 
(hlovdal) localhost:/work/2011/perl_unicode>./json_malformed_utf8.pl | ./uniquote 
Uncaught exception: malformed UTF-8 character in JSON string, at character offset 20 (before "\x{5824}eg" }\n") at ./json_malformed_utf8.pl line 96. 
at ./json_malformed_utf8.pl line 46 
     main::__ANON__('malformed UTF-8 character in JSON string, at character offset...') called at ./json_malformed_utf8.pl line 96 
JSON->backend->is_pp = 0, JSON->backend->is_xs = 1 
is_utf8(): 1, is_utf8(1): 1. $json_text = { "my_test" : "hei p\N{U+E5} deg" } 
string is valid utf8 
(hlovdal) localhost:/work/2011/perl_unicode>./json_malformed_utf8.pl -nfd | ./uniquote 
Uncaught exception: Wide character in subroutine entry at ./json_malformed_utf8.pl line 96. 
at ./json_malformed_utf8.pl line 46 
     main::__ANON__('Wide character in subroutine entry at ./json_malformed_utf8.pl line 96.\x{a}') called at ./json_malformed_utf8.pl line 96 
JSON->backend->is_pp = 0, JSON->backend->is_xs = 1 
is_utf8(): 1, is_utf8(1): 1. $json_text = { "my_test" : "hei pa\N{U+30A} deg" } 
string is valid utf8 
(hlovdal) localhost:/work/2011/perl_unicode>./json_malformed_utf8.pl -water 
JSON->backend->is_pp = 0, JSON->backend->is_xs = 1 
is_utf8(): 1, is_utf8(1): 1. $json_text = { "water" : "水" } 
string is valid utf8 
Uncaught exception: Wide character in subroutine entry at ./json_malformed_utf8.pl line 96. 
at ./json_malformed_utf8.pl line 46 
     main::__ANON__('Wide character in subroutine entry at ./json_malformed_utf8.pl line 96.\x{a}') called at ./json_malformed_utf8.pl line 96 
(hlovdal) localhost:/work/2011/perl_unicode>./json_malformed_utf8.pl -water | ./uniquote 
Uncaught exception: Wide character in subroutine entry at ./json_malformed_utf8.pl line 96. 
at ./json_malformed_utf8.pl line 46 
     main::__ANON__('Wide character in subroutine entry at ./json_malformed_utf8.pl line 96.\x{a}') called at ./json_malformed_utf8.pl line 96 
JSON->backend->is_pp = 0, JSON->backend->is_xs = 1 
is_utf8(): 1, is_utf8(1): 1. $json_text = { "water" : "\N{U+6C34}" } 
string is valid utf8 
(hlovdal) localhost:/work/2011/perl_unicode>./json_malformed_utf8.pl -water --nfd | ./uniquote 
Uncaught exception: Wide character in subroutine entry at ./json_malformed_utf8.pl line 96. 
at ./json_malformed_utf8.pl line 46 
     main::__ANON__('Wide character in subroutine entry at ./json_malformed_utf8.pl line 96.\x{a}') called at ./json_malformed_utf8.pl line 96 
JSON->backend->is_pp = 0, JSON->backend->is_xs = 1 
is_utf8(): 1, is_utf8(1): 1. $json_text = { "water" : "\N{U+6C34}" } 
string is valid utf8 
(hlovdal) localhost:/work/2011/perl_unicode>rpm -q perl perl-JSON perl-JSON-XS 
perl-5.12.4-159.fc15.x86_64 
perl-JSON-2.51-1.fc15.noarch 
perl-JSON-XS-2.30-2.fc15.x86_64 
(hlovdal) localhost:/work/2011/perl_unicode> 

uniquoteがhttp://training.perl.com/scripts/uniquote


Updateからです与える:ソリューションを強調表示するブライアンへ

感謝を。予想されるようになりました次のようなJSONに渡されようとしているもののためにすべての通常の文字列のjson_textjson_bytesを使用するソースを更新すると動作します:

my $json_bytes = encode('UTF-8', $json_text); 
my $hash_ref1 = JSON->new->utf8->decode($json_bytes); 

私はJSONモジュールのドキュメントが極めてだと思うことを言わなければなりません不明瞭で部分的に誤解を招く。

"text"(少なくとも私にとって)というフレーズは文字列を意味します。 したがって、$perl_scalar = decode_json $json_textを読むとき、私は を期待しています。json_textはUTF-8でエンコードされた文字列です。 ドキュメントを完全に再読しました。「decode_json ...はUTF-8(バイナリ)文字列を受け取り、UTF-8でエンコードされたJSONテキストである を解析しようとしています」しかし、それはまだ私の意見ではっきりしていません。いくつかの追加の非ASCII 文字を持つ言語を使用して、私の背景から

が、私はあなたが使用されているコード ページを推測しなければならなかった日に戻って覚えて、電子メールはちょうど 8ビット目のストリッピングすることにより、テキストを不自由に使用されます文字列の文脈における "バイナリ"は、7ビットASCIIドメイン外の文字を含む文字列 を意味しました。しかし、実際に は "バイナリ"とは何ですか?すべての文字列がコアレベルでバイナリではありませんか?

ドキュメントでは、「簡単かつ高速なインターフェイス(期待値/生成UTF-8)」と「正しいUnicode処理」が最初に指摘されています。バイトシーケンス。私は の著者に少なくともこれをもっと明確にするよう依頼します。

+0

TomのUnicodeユーティリティは、[Unicode :: Tussle](http://search.cpan.org/dist/Unicode-Tussle)としても入手できます。 –

答えて

12

私はKnow the difference between character strings and UTF-8 stringsに私の答えを展開します。


JSONドキュメントを読んだことで、これらの機能は文字列を必要としないと思いますが、それはそれを与えることです。代わりに、 "UTF-8バイナリ文字列"が必要です。それは私には奇妙に思えますが、私はあなたがプログラムに直接入力するものの代わりに、HTTPメッセージから直接入力を受け取ることがほとんどだと推測しています。私はあなたの文字列のUTF-8エンコードされたバージョンだバイト文字列を作るので、これは動作します:

use v5.14; 

use utf8;             
use warnings;            
use feature  qw<unicode_strings>; 

use Data::Dumper; 
use Devel::Peek; 
use JSON; 

my $filename = 'hei.txt'; 
my $char_string = qq({ "my_test" : "hei på deg" }); 
open my $fh, '>:encoding(UTF-8)', $filename; 
print $fh $char_string; 
close $fh; 


{ 
say '=' x 70; 
my $byte_string = qq({ "my_test" : "hei p\303\245 deg" }); 
print "Byte string peek:------\n"; Dump($byte_string); 
decode($byte_string); 
} 


{ 
say '=' x 70; 
my $raw_string = do { 
    open my $fh, '<:raw', $filename; 
    local $/; <$fh>; 
    }; 
print "raw string peek:------\n"; Dump($raw_string); 

decode($raw_string); 
} 

{ 
say '=' x 70; 
my $char_string = do { 
    open my $fh, '<:encoding(UTF-8)', $filename; 
    local $/; <$fh>; 
    }; 
print "char string peek:------\n"; Dump($char_string); 

decode($char_string); 
} 

sub decode { 
    my $string = shift; 

    my $hash_ref2 = eval { decode_json($string) }; 
    say "Error in sub form: [email protected]" if [email protected]; 
    print Dumper($hash_ref2); 

    my $hash_ref1 = eval { JSON->new->utf8->decode($string) }; 
    say "Error in method form: [email protected]" if [email protected]; 
    print Dumper($hash_ref1); 
    } 

出力は文字列が動作しないことを示しているが、バイト文字列のバージョンが実行します。

====================================================================== 
Byte string peek:------ 
SV = PV(0x100801190) at 0x10089d690 
    REFCNT = 1 
    FLAGS = (PADMY,POK,pPOK) 
    PV = 0x100209890 " { \"my_test\" : \"hei p\303\245 deg\" } "\0 
    CUR = 31 
    LEN = 32 
$VAR1 = { 
      'my_test' => "hei p\x{e5} deg" 
     }; 
$VAR1 = { 
      'my_test' => "hei p\x{e5} deg" 
     }; 
====================================================================== 
raw string peek:------ 
SV = PV(0x100839240) at 0x10089d780 
    REFCNT = 1 
    FLAGS = (PADMY,POK,pPOK) 
    PV = 0x100212260 " { \"my_test\" : \"hei p\303\245 deg\" } "\0 
    CUR = 31 
    LEN = 32 
$VAR1 = { 
      'my_test' => "hei p\x{e5} deg" 
     }; 
$VAR1 = { 
      'my_test' => "hei p\x{e5} deg" 
     }; 
====================================================================== 
char string peek:------ 
SV = PV(0x10088f3b0) at 0x10089d840 
    REFCNT = 1 
    FLAGS = (PADMY,POK,pPOK,UTF8) 
    PV = 0x1002017b0 " { \"my_test\" : \"hei p\303\245 deg\" } "\0 [UTF8 " { "my_test" : "hei p\x{e5} deg" } "] 
    CUR = 31 
    LEN = 32 
Error in sub form: malformed UTF-8 character in JSON string, at character offset 21 (before "\x{5824}eg" } ") at utf-8.pl line 51. 

$VAR1 = undef; 
Error in method form: malformed UTF-8 character in JSON string, at character offset 21 (before "\x{5824}eg" } ") at utf-8.pl line 55. 

$VAR1 = undef; 
あなたがあなたのプログラムに直接入力あなたの文字列を取る、とUTF-8エンコードされたバイトの文字列に変換し、それが動作するのであれば、

を:

use v5.14; 

use utf8;             
use warnings;            
use feature  qw<unicode_strings>; 

use Data::Dumper; 
use Encode qw(encode_utf8); 
use JSON; 

my $char_string = qq({ "my_test" : "hei på deg" }); 

my $string = encode_utf8($char_string); 

decode($string); 

sub decode { 
    my $string = shift; 

    my $hash_ref2 = eval { decode_json($string) }; 
    say "Error in sub form: [email protected]" if [email protected]; 
    print Dumper($hash_ref2); 

    my $hash_ref1 = eval { JSON->new->utf8->decode($string) }; 
    say "Error in method form: [email protected]" if [email protected]; 
    print Dumper($hash_ref1); 
    } 

私は、JSONは十分にスマートされるべきだと思いますこれに対処するためには、このレベルで考える必要はありませんが、それはそれまでの方法です(これまでのところ)。

+0

Re "それは私にとって奇妙に思える"、それは全く私には奇妙に見えません。 XMLパーサに渡す前にXMLをデコードしないでください。あなたはPerlにそれを渡す前にPerlプログラムを解読しません。これらのパーサーで復号化されたテキストが必要な場合は、ファイルを2回解析する必要があります.1回はエンコードを決定し、もう1回は実際の解析を行います。まさに逆ですが、 'decode'というメソッドに渡す前に何かをデコードするのは私にとって非常に奇妙です。 – ikegami

+0

Re:「JSONはこれに対処するには十分にスマートでなければならないと思います」、それは不可能です。何かが既にデコードされているかどうかを知る方法はありません。 – ikegami

+0

私はそれを期待していなかったので、私には奇妙に思えます。私は物事がこのレベルの思考なしでちょうどうまくいってほしいです。 OSCONでは、私の主な不満は、スカラーがあるだけで、バイナリ文字列か文字列かはわかりません。十分にスマートであることについては、それは不可能かもしれないが、私はまだその機能が好きです。私は何が起こっているのだろうか。 PerlがUTF-8文字列としてマークされている場合、JSONは実際に何を使用していますか?私はもっ​​と調査しなければならない。 –

5

ドキュメントは

$perl_hash_or_arrayref = decode_json $utf8_encoded_json_text; 

を言って、まだあなたはdecode_jsonに渡す前に入力をデコードするためにあなたの力ですべてを行います。

use strict; 
use warnings; 
use utf8; 

use Data::Dumper qw(Dumper); 
use Encode  qw(encode); 
use JSON   qw(); 

for my $json_text (
    qq{{ "my_test" : "hei på deg" }\n}, 
    qq{{ "water" : "水" }\n}, 
) { 
    my $json_utf8 = encode('UTF-8', $json_text); # Counteract "use utf8;" 
    my $data = JSON->new->utf8->decode($json_utf8); 

    local $Data::Dumper::Useqq = 1; 
    local $Data::Dumper::Terse = 1; 
    local $Data::Dumper::Indent = 0; 
    print(Dumper($data), "\n"); 
} 

出力:

{"my_test" => "hei p\x{e5} deg"} 
{"water" => "\x{6c34}"} 

PS —あなたは、単純な問題を示すために、コードの2つのページを持っていなかった場合、あなたを助けるために容易になるだろう。

-1

私は答えを通して偶然に起こったと信じています!

  • かなりの記号はのWebSocketで来て、JSON :: XS
  • 細かい仕事:: decode_jsonが出
  • (つまりJSONのくそのWRITE_FILEがあまりにも狂気になり、I手立て "ワイド文字"
  • を死ぬん

多くのDIYが必要です。ここに私のIOコマンドです:のWebSocketで来る

sub spurt { 
my $self = shift; 
my $file = shift; 
my $stuff = shift; 
say "Hostinfo: spurting $file (".length($stuff).")"; 
    open my $f, '>', $file || die "O no $!"; 
binmode $f, ':utf8'; 
print $f $stuff."\n"; 
#         slurp instead does: 
#         my $m = join "", <$f>; 
close $f; 
} 

次にJSONにデコードもの:ちょうどループのための私を投げた

start_timer(); 
    $hostinfo->spurt('/tmp/elvis', $msg); 
    my $convert = q{perl -e 'use YAML::Syck; use JSON::XS; use File::Slurp;} 
    .q{print " - reading json from /tmp/elvis\n";} 
    .q{my $j = read_file("/tmp/elvis");} 
    .q{print "! json already yaml !~?\n$j\n" if $j =~ /^---/s;} 
    .q{print " - convert json -> yaml\n";} 
    .q{my $d = decode_json($j);} 
    .q{print " - write yaml to /tmp/elvis\n";} 
    .q{DumpFile("/tmp/elvis", $d);} 
    .q{print " - done\n";} 
    .q{'}; 
    `$convert`; 

    eval { 
    $j = LoadFile('/tmp/elvis'); 

    while (my ($k, $v) = each %$j) { 
     if (ref \$v eq "SCALAR") { 
      $j->{$k} = Encode::decode_utf8($v); 
     } 
    } 
    }; 
    say "Decode in ".show_delta(); 

- 私は塩を嗅い必要があるかもしれません!

しかし、私は奇妙なシンボルがディスクを移動するために完全にクリアされている唯一の方法 - perl - websocket/json - JS/HTML/codemirror/whateverそしてback。シンボルは:utf8レベルまたはモードでspurtでディスクに書き込まなければなりません。モジョや一緒に使っているものは、perlのライナーですべてうまく動作するので、それを壊していると思います。私はそれをすべて修正できることを知っています。私はとても忙しいです。

おそらくどこかシンプルなものがありますが、私はそれを疑っています。人生は時々私を圧倒する、私は宣言する!

これよりも狂気が少なくなると、ディスク上の文字は壊れますが、perlやwebsocketの反対側の文字は壊れます。

関連する問題