Сгенерированные Perl строки UTF8 в JSON повреждены на стороне клиента - PullRequest
0 голосов
/ 15 февраля 2019

У меня есть сценарий Perl CGI, который обращается к тайскому языку, строкам UTF-8 из базы данных PostgreSQL и возвращает их веб-интерфейсу в виде JSON.Строки в порядке, когда я получаю их из БД и после того, как кодирую их как JSON (на основе записи в файл журнала).Тем не менее, когда клиент получает их, они повреждены, например:

имя_компонента "¸ \ u0094ภาภ© ี "

Очевидно, что некоторые символы конвертируются в escape-последовательности Unicode, но не все.

Я действительно мог бы использовать некоторые предложения, как решить эту проблему.

Ниже приведен упрощенный фрагмент кода.Я использую utf8 и utf8 :: all, а также JSON.

Заранее благодарен за любую помощь, которую вы можете предоставить.

my $dataId = $cgi->param('dataid');
my $table = "uploadpoints";
my $sqlcommand = "select id,featurename from $table where dataid=$dataId;";
my $stmt = $gDbh->prepare($sqlcommand);
my $numrows = $stmt->execute;
# print JSON header
print <<EOM;
Content-type: application/json; charset="UTF-8"


EOM
my @retarray;
for (my $i = 0; ($i < $numrows); $i=$i+1)
{
    my $hashref = $stmt->fetchrow_hashref("NAME_lc");
    #my $featurename = $hashref->{'featurename'};
    #logentry("Point $i feature name is: $featurename\n");
    push @retarray,$hashref;
}
my $json = encode_json (\@retarray);
logentry("JSON\n $json");
print $json;

Я изменил иУпрощенный пример, теперь выполняется локально, а не через вызов браузера:

my $dataId = 5; 
my $table = "uploadpoints";
my $sqlcommand = "select id,featurename from $table where dataid=$dataId and id=75;";
my $stmt = $gDbh->prepare($sqlcommand);
my $numrows = $stmt->execute;
my @retarray;
for (my $i = 0; ($i < $numrows); $i=$i+1)
{
    my $hashref = $stmt->fetchrow_hashref("NAME_lc");
    my $featurename = $hashref->{'featurename'};
    print "featurename $featurename\n";
    push @retarray,$hashref;
}
my $json = encode_json (\@retarray);
print $json;

Используя hexdump, как в примере Стефана, я определил, что данные, считанные из базы данных, уже находятся в UTF-8.Похоже, что они перекодируются в методе кодирования JSON.Но почему?

Данные в JSON используют ровно в два раза больше байтов, чем исходный UTF-8.

 perl testcase.pl | hexdump -C
00000000  66 65 61 74 75 72 65 6e  61 6d 65 20 e0 b9 82 e0  |featurename ....|
00000010  b8 a3 e0 b8 87 e0 b9 80  e0 b8 a3 e0 b8 b5 e0 b8  |................|
00000020  a2 e0 b8 99 e0 b9 81 e0  b8 88 e0 b9 88 e0 b8 a1  |................|
00000030  e0 b8 88 e0 b8 b1 e0 b8  99 e0 b8 97 e0 b8 a3 e0  |................|
00000040  b9 8c 0a 5b 7b 22 66 65  61 74 75 72 65 6e 61 6d  |...[{"featurenam|
00000050  65 22 3a 22 c3 a0 c2 b9  c2 82 c3 a0 c2 b8 c2 a3  |e":"............|
00000060  c3 a0 c2 b8 c2 87 c3 a0  c2 b9 c2 80 c3 a0 c2 b8  |................|
00000070  c2 a3 c3 a0 c2 b8 c2 b5  c3 a0 c2 b8 c2 a2 c3 a0  |................|
00000080  c2 b8 c2 99 c3 a0 c2 b9  c2 81 c3 a0 c2 b8 c2 88  |................|
00000090  c3 a0 c2 b9 c2 88 c3 a0  c2 b8 c2 a1 c3 a0 c2 b8  |................|
000000a0  c2 88 c3 a0 c2 b8 c2 b1  c3 a0 c2 b8 c2 99 c3 a0  |................|
000000b0  c2 b8 c2 97 c3 a0 c2 b8  c2 a3 c3 a0 c2 b9 c2 8c  |................|
000000c0  22 2c 22 69 64 22 3a 37  35 7d 5d                 |","id":75}]|
000000cb

Есть еще предложения?Я попытался использовать декодирование в строке UTF, но получил ошибки, связанные с широкими символами.

Я прочитал рекомендованный ответ Тома Кристиансона, а также его учебники по Юникоду, но я признаю, что многое из этого пошло мне на ум.Кроме того, кажется, что моя проблема значительно более ограничена.

Я действительно задавался вопросом, было ли получение значения хеша и присвоение его нормальной переменной каким-то образом автоматически декодировать или кодировать.Я не очень понимаю, когда Perl использует свой внутренний символьный формат, в отличие от того, когда он сохраняет внешнюю кодировку.

ОБНОВЛЕНИЕ С РЕШЕНИЕМ

Оказывается, поскольку строка, полученная из БД, уже находится вUTF-8, мне нужно использовать «to_json», а не «encode_json».Это решило проблему.Хотя много узнал об обработке Perl Unicode в процессе ...

Также рекомендую: http://perldoc.perl.org/perluniintro.html

Очень четкое изложение.

1 Ответ

0 голосов
/ 15 февраля 2019

ПРИМЕЧАНИЕ: вы, вероятно, также должны прочитать этот ответ , что делает мой ответ ниже по сравнению с: -)

Проблема в том, что вы должныубедитесь, в каком формате находится каждая строка, иначе вы получите неправильные преобразования.При обработке UTF-8 строка может иметь два формата:

Если задействован ввод / вывод, вы такженужно знать, выполняет ли уровень ввода / вывода де / кодирование UTF-8 или нет.Для терминального ввода-вывода вы также должны рассмотреть, понимает ли он UTF-8 или нет.Обе вместе взятые могут затруднить получение значимых отладочных распечаток из вашего кода.

Если ваш код Perl должен обрабатывать строки UTF-8 после чтения их из исходного кода, вы должны убедиться, что они находятся во внутреннем Perlформат.В противном случае вы получите удивительный результат, когда вызовете код, который ожидает строки Perl, а не строки необработанных октетов.

Я пытаюсь показать это в своем примере кода:

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

use JSON;

open(my $utf8_stdout, '>& :encoding(UTF-8)', \*STDOUT)
    or die "can't reopen STDOUT as utf-8 file handle: $!\n";

my $hex = "C480";
print "${hex}\n";

my $raw = pack('H*', $hex);
print STDOUT       "${raw}\n";
print $utf8_stdout "${raw}\n";

my $decoded;
utf8::decode($decoded = $raw);
print STDOUT       ord($decoded), "\n";
print STDOUT       "${decoded}\n"; # Wide character in print at...
print $utf8_stdout "${decoded}\n";

my $json = JSON->new->encode([$decoded]);
print STDOUT       "${json}\n"; # Wide character in print at...
print $utf8_stdout "${json}\n";

$json = JSON->new->utf8->encode([$decoded]);
print STDOUT       "${json}\n";
print $utf8_stdout "${json}\n";

exit 0;

Копировать и вставить измой терминал (который поддерживает UTF-8).Посмотрите внимательно на различия между строками:

$ perl dummy.pl
C480
Ā
Ä
256
Wide character in print at dummy.pl line 21.
Ā
Ā
Wide character in print at dummy.pl line 25.
["Ā"]
["Ā"]
["Ā"]
["Ä"]

Но сравните это со следующим, где STDOUT не является терминалом, но передается другой программе.В шестнадцатеричном дампе всегда отображается «c4 80», т.е. в кодировке UTF-8.

$ perl dummy.pl | hexdump -C
Wide character in print at dummy.pl line 21.
Wide character in print at dummy.pl line 22.
Wide character in print at dummy.pl line 25.
Wide character in print at dummy.pl line 26.
00000000  43 34 38 30 0a c4 80 0a  c4 80 0a 5b 22 c4 80 22  |C480.......[".."|
00000010  5d 0a 5b 22 c4 80 22 5d  0a 43 34 38 30 0a c4 80  |].[".."].C480...|
00000020  0a 32 35 36 0a c4 80 0a  5b 22 c4 80 22 5d 0a 5b  |.256....[".."].[|
00000030  22 c4 80 22 5d 0a                                 |".."].|
00000036
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...