Perl многобайтовая кодировка символов для HTML - PullRequest
2 голосов
/ 18 января 2020

Мне передают строку, такую ​​как:

my $x = "Zakłady Kuźnicze";

Если вы посмотрите ближе, вы увидите, что эти две странные буквы на самом деле состоят из двух байтов:

foreach (split(//, $x)) { print $_.' '.ord($_)."\n"; }

Z 90
a 97
k 107
� 197
� 130
a 97
d 100
y 121
  32
K 75
u 117
� 197
� 186
n 110
i 105
c 99
z 122
e 101

Я хочу преобразовать это в кодированный HTML, используя коды, описанные здесь: https://www.w3schools.com/charsets/ref_utf_latin_extended_a.asp

Так что мне нужна функция, которая:

print encode_it($x)."\n";

дает:

Zakłady Kužnice

Я пробовал HTML::Entities::encode и HTML::Entities::encode_numeric, но эти выходы:

Zakłady Kuźnicze

Zakłady Kuźnicze

Что не помогает, он отображается как:

Zakłady Kuźnicze

Может кто-нибудь посоветовать, как этого добиться?

РЕДАКТИРОВАТЬ:

Как Ikegami показало, что работает, если используется use utf8 И в программе установлена ​​строка:

perl -e 'use utf8; chomp; printf "%X\n", ord for split //, "Zakłady Kuźnicze"'
5A
61
6B
142
61
64
79
20
4B
75
17A
6E
69
63
7A
65

... но мой ввод фактически поступает через STDIN, и он не работает из STDIN:

echo "Zakłady Kuźnicze" | perl -ne 'use utf8; chomp; printf "%X\n", ord for split //'
5A
61
6B
C5
82
61
64
79
20
4B
75
C5
BA
6E
69
63
7A
65

Какая тонкость мне здесь не хватает?

Ответы [ 2 ]

4 голосов
/ 18 января 2020

Perl ожидает, что источником будет ASCII [1] (no utf8;, по умолчанию) или UTF-8 (use utf8;). Похоже, у вас есть файл, закодированный с использованием UTF-8, но вы не сказали Perl об этом, поэтому он видит

my $x = "Zak\xC5\x82ady Ku\xC5\xBAnicze";

вместо ожидаемого

my $x = "Zak\x{142}ady Ku\x{17A}nicze";

Пример ( Терминал UTF-8):

$ diff -U 0 \
   <( perl -e'no utf8;  printf "%X\n", ord for split //, "Zakłady Kuźnicze"' ) \
   <( perl -e'use utf8; printf "%X\n", ord for split //, "Zakłady Kuźnicze"' )
--- /dev/fd/63  2020-01-17 20:04:23.407591294 -0800
+++ /dev/fd/62  2020-01-17 20:04:23.407591294 -0800
@@ -4,2 +4 @@
-C5
-82
+142
@@ -12,2 +11 @@
-C5
-BA
+17A

Добавить use utf8;.


  1. 8-битная чистая версия ASCII, означающая, что любой байт с 8-м битом установлен в строке или регулярном выражении литерала получается символ с тем же значением.
2 голосов
/ 18 января 2020

Как сказал @ikegami, use utf8; будет декодировать ваш исходный код из UTF-8, так что строковые литералы и другие символы могут быть интерпретированы как задумано. Как и исходный код, входные данные для вашего кода также в байтах, и обычно в кодировке UTF-8, если это текст. Таким образом, в зависимости от того, откуда он исходит, у вас есть несколько вариантов для его преобразования в полезные символы. Ниже перечислены различные варианты, вам нужен только один для определенного потока ввода.

From STDIN:

use open ':std', IN => ':encoding(UTF-8)'; # also affects read filehandles opened in this scope
use open ':std', ':encoding(UTF-8)'; # also affects STDOUT, STDERR, and all filehandles opened in this scope
binmode *STDIN, ':encoding(UTF-8)'; # STDIN only

Или эти переключатели для oneliners:

-CI # STDIN only
-CS # STDIN, STDOUT, STDERR
-Mopen=':std,IN,:encoding(UTF-8)' # equivalent to first "use open" above

From ручки, которые вы открываете сами:

use open IN => ':encoding(UTF-8)'; # all read handles opened in this scope
use open ':encoding(UTF-8)'; # also affects write handles
open my $fh, '<:encoding(UTF-8)', 'example.txt' or die "Failed to open example.txt: $!";
binmode $fh, ':encoding(UTF-8)'; # to set on already opened handle

Или эти переключатели для oneliners:

-Ci # read handles only
-CD # all handles opened
-Mopen='IN,:encoding(UTF-8)' # equivalent to first "use open" above

Указанные выше опции use open и -C также применимы к ARGV (ручка, используемая -n, -p или оператор <> / readline для чтения имен файлов, переданных в качестве аргументов - это отличается от того, когда он используется для чтения STDIN). Переключатели -C можно комбинировать, например, -CSD установит его для STDIN / OUT / ERR, а также для всех открытых дескрипторов.

Наконец, вы можете декодировать сами данные после чтения, а не влиять на какие-либо дескрипторы глобально (ниже, предполагая, что данные в $_):

utf8::decode($_) or die "Invalid UTF-8"; # in place, does not require "use utf8"
$_ = Encode::decode('UTF-8', $_); # with Encode loaded
$_ = Encode::Simple::decode_utf8($_); # with Encode::Simple loaded

Просто запомните, хотите ли вы выводить такие декодированные символы или символы из литералов с use utf8;, установленными для вашего исходного кода, STDOUT, STDERR и другие дескрипторы записи требуют такой же обработки, или вам необходимо закодировать данные в UTF-8 перед печатью.

Некоторые полезные ссылки:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...