2011-08-01 2 views
11

this questionthis answer (다른 질문으로)과 관련하여 저는 여전히 JSON으로 UTF-8을 처리 할 수 ​​없습니다.perl : 잡히지 않는 예외 : JSON 문자열의 형식이 잘못된 UTF-8 문자

필자는 최상의 전문가의 권장 사항에 따라 필요한 모든 부두가 실행되도록 노력했으며 가능한 한 문자열이 유효한 것으로 표시되고 가능하면 UTF-8로 표시되어 있는지 확인하려고 노력했습니다. 하지만 여전히 펄 중 하나

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


업데이트에서입니다 제공 : 솔루션을 강조하기위한 브라이언에

감사합니다. 지금처럼 예상 작품을 다음과 같이 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)"및 "올바른 유니 코드 처리"는 "기능"아래의 첫 번째 항목으로 문자열을 원하지 않고 근처에 언급하지 않고 바이트 순서 작성자에게 최소한이 내용을 명확하게하도록 요청할 것입니다.

+0

Tom의 유니 코드 유틸리티는 [Unicode :: Tussle] (http://search.cpan.org/dist/Unicode-Tussle)로도 제공됩니다. –

답변

12

나는 내 대답을 Know the difference between character strings and UTF-8 strings으로 확장합니다.


JSON 문서를 읽었을 때, 나는이 함수들이 문자열을 원하지 않는다고 생각하지만, 그게 당신이주고 싶어하는 것입니다. 대신, 그들은 "UTF-8 2 진 문자열"을 원합니다. 그것은 이상하게 보입니다. 그러나 저는 여러분이 여러분의 프로그램에 직접 입력하는 것 대신에 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 프로그램을 디코딩하지 마십시오. 이러한 파서에 해독 된 텍스트가 필요한 경우 파일을 두 번 파싱해야합니다. 한 번 인코딩을 결정하고 한 번 실제 파싱을 수행해야합니다. 정반대로,'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 — 간단한 문제를 보여주기 위해 코드의 두 페이지를 가지고 있지 않은 경우 그것은 당신을 도울 쉬울 것이다.

-1

나는 대답을 통해 우연히 일어난 일이라고 생각한다!

  • 꽤 기호는 웹 소켓에 와서 잘 작동
  • JSON이 :: XS는 :: decode_json이 방법은 밖으로
  • 가 json으로 이놈의
  • (write_file도 제정신이 간다
  • "와이드 문자를"죽으면
  • , I

DIY가 많이 필요합니다. 여기 내 IO 명령과 같다 : 웹 소켓으로 제공

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/뭐든간에. 심볼은 : utf8 레벨 또는 모드와 함께 spurt로 디스크에 기록되어야합니다. 나는 모조 또는 내가 함께 사용하고있는 어떤 것이 파울 하나의 라이너에서 모두 잘 작동하는 것처럼 깨뜨린다고 생각합니다. 그리고 나는 그것을 모두 고칠 수 있다는 것을 알고 있습니다. 나는 너무 바쁘다.

아마도 어딘가에 단순한 무언가가있을 것입니다.하지만 나는 그것을 의심합니다. 인생은 때때로 나를 압도합니다. 나는 선언합니다!

이 디스크보다 광감이 적기 때문에 디스크의 문자는 깨지지 만 perl과 websocket의 다른 끝에있는 문자는 작동하지 않습니다.

관련 문제