Perl ord и chr, работающие с unicode

к моему ужасу я только что узнал, что chr не работает с Unicode, хотя это делает что-то. на главной странице все чисто

возвращает символ, представленный в наборе символов. Например, chr (65) "является" A " в ASCII или Unicode, а chr(0x263a) - это смайлик Unicode.

действительно, я могу напечатать смайлик, используя

perl -e 'print chr(0x263a)'

но такие вещи, как chr(0x00C0) не работают. Я смотрите, что мой perl v5.10.1 немного древний, но когда я вставляю различные странные буквы в исходный код, все в порядке.

я пробовал смешные вещи, такие как use utf8 и use encoding 'utf8', Я не пробовал смешные вещи, как use v5.12 и use feature 'unicode_strings' поскольку они не работают с моей версией, я дурачился с Encode::decode чтобы узнать, что мне не нужно декодирование, поскольку у меня нет массива байтов для декодирования. Я прочитал гораздо больше документации, чем когда-либо прежде, и нашел довольно много интересных вещей, но ничего полезного. Это похоже на своего рода Ошибка Unicode но нет подходящего решения. Более того, меня не волнует вся семантика строк, все, что мне нужно, это тривиальная функция.

так как я могу преобразовать число в строку, состоящую из одного символа, соответствующего его, так, что например real_chr(0xC0) eq 'À' держит?


первый ответ, который у меня есть, объясняет все об Ио, но я все еще не понимаю почему?--16-->

#!/usr/bin/perl -w
use strict;
use utf8;
use encoding 'utf8';

print chr(0x00C0) eq 'À' ? 'eq1' : 'ne1', " - ", chr(0x263a) eq '☺' ? 'eq1' : 'ne1', "n";

print 'À' =~ /w/ ? "match1" : "no_match1", " - ", chr(0x00C0) =~ /w/ ? "match2" : "no_match2", "n";

печать

ne1 - eq1
match1 - no_match2

это означает, что введенные вручную 'À' отличается от chr(0x00C0). Более того, первое - это слово составного характера (правильно!), а вторые-нет (но должны быть!).

1 ответов


во-первых,

perl -le'print chr(0x263A);'

глючит. Perl даже говорит вам об этом:

Wide character in print at -e line 1.

это не квалифицируется как"работа". Поэтому, хотя они отличаются тем, как не обеспечить то, что вы хотите, ни одно из следующих не дает вам то, что вы хотите:

perl -le'print chr(0x263A);'

perl -le'print chr(0x00C0);'

чтобы правильно вывести кодировку UTF-8 этих кодовых точек Unicode, вам нужно сказать Perl для кодирования точек Unicode с помощью UTF-8.

$ perl -le'use open ":std", ":encoding(UTF-8)"; print chr(0x263A);'
☺

$ perl -le'use open ":std", ":encoding(UTF-8)"; print chr(0x00C0);'
À

теперь на "причина."

файловый дескриптор может передавать только байты, поэтому, если вы не скажете иначе, Perl-файл обрабатывает ожидаемые байты. Это означает строку, которую вы предоставляете print не может содержать ничего, кроме байтов, или, другими словами, не может содержать символов более 255. Выход именно то, что вы предоставляете:

$ perl -e'print map chr, 0x00, 0x65, 0xC0, 0xF0' | od -t x1
0000000 00 65 c0 f0
0000004

это полезно. Это отличается от того, что вы хотите, но это не делает его неправильным. Если вы хотите что-то другое, вам просто нужно сказать Perl, что вы хотеть.

добавить :encoding layer, дескриптор теперь ожидает строку символов Юникода или, как я его называю, "текст". Слой сообщает Perl, как преобразовать текст в байты.

$ perl -e'
   use open ":std", ":encoding(UTF-8)";
   print map chr, 0x00, 0x65, 0xC0, 0xF0, 0x263a;
' | od -t x1
0000000 00 65 c3 80 c3 b0 e2 98 ba
0000011

ваше право, что chr не знает и не заботится о Unicode. Как length, substr, ord и reverse, chr реализует базовую строковую функцию, а не функцию Unicode. Это не означает, что его нельзя использовать для работы с текстовой строкой. Как вы видели, проблема была не в chr но с тем, что вы сделали со строкой после того, как вы ее построили.

символ-это элемент строки, а символ-число. Это означает, что строка-это просто последовательность чисел. Рассматриваете ли вы эти числа как кодовые точки Юникода (текст), упакованные IP-адреса или измерения температуры, полностью зависит от вас и функций, которым вы передаете строки.

вот несколько примеров операторов, которые присваивают смысл строки, которые они получают как операнды:

  • m// ожидает строку кодовых точек Unicode.
  • connect ожидает последовательность байтов, которые представляют собой sockaddr_in структура.
  • print с ручкой без :encoding ожидайте последовательность байтов.
  • print с ручкой с :encoding ожидайте последовательность кодовых точек Unicode.
  • etc

так как я могу преобразовать число в строка, состоящая из одного символа, соответствующего ей, так что, например, real_chr(0xC0) eq 'À' выполняется?

chr(0xC0) eq 'À' не согласен. Вы не забыли сказать Perl, что закодировали исходный код с помощью UTF-8, используя use utf8;? Если вы не сказали Perl, Perl фактически видит двухсимвольную строку в RHS.


по вопросу Ты добавил:

есть проблемы с encoding ПРАГМА. Я рекомендую не использовать его. Вместо, использовать

use open ':std', ':encoding(UTF-8)';

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

chr(0x00C0) =~ /\w/

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

use 5.014;    # use 5.012; *might* suffice.

обходной путь, который работает до 5.8:

my $x = chr(0x00C0);
utf8::upgrade($x);
$x =~ /\w/