Spec-Zone.ru › Perl 5.38

perluniintro

СОДЕРЖАНИЕ

  • ИМЯ
  • ОПИСАНИЕ
    • Unicode
    • Поддержка Unicode в Perl
    • Модель Unicode в Perl
    • Unicode и EBCDIC
    • Создание Unicode
      • Ограничения предыдущих версий
    • Обработка Unicode
    • Записи старых кодировок
    • Ввод/вывод Unicode
    • Отображение Unicode как текста
    • Особые случаи
    • Расширенные темы
    • Разное
    • Вопросы с ответами
    • Шестнадцатеричная нотация
    • Дополнительные ресурсы
  • UNICODE В СТАРЫХ ВЕРСИЯХ PERL
  • СМОТРИТЕ ТАКЖЕ
  • ПРИЗНАТЕЛЬНОСТИ
  • АВТОР, АВТОРСКИЕ ПРАВА И ЛИЦЕНЗИЯ

ИМЯ

perluniintro - Введение в Unicode для Perl

ОПИСАНИЕ

Этот документ дает общее представление об Unicode и о том, как использовать Unicode в Perl. См. "Дополнительные ресурсы" для ссылок на более подробные материалы по Unicode.

Unicode

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

Unicode и ISO/IEC 10646 — это согласованные стандарты, которые объединяют почти все другие современные стандарты наборов символов, охватывающие более 80 письменных систем и сотни языков, включая все коммерчески важные современные языки. Кодируются все символы из самых больших китайских, японских и корейских словарей. В конечном итоге стандарты охватят практически все символы более чем в 250 письменных системах и тысячах языков. Unicode 1.0 был выпущен в октябре 1991 года, а 6.0 — в октябре 2010 года.

Unicode-символ — это абстрактная сущность. Он не связан с какой-либо конкретной шириной целого числа, особенно не с языком программирования C char. Unicode не зависит от языка и способа отображения: он не кодирует язык текста, и он обычно не определяет шрифты или другие детали графического форматирования. Unicode работает с символами и с текстом, построенным из этих символов.

Unicode определяет символы, такие как LATIN CAPITAL LETTER A или GREEK SMALL LETTER ALPHA, и уникальные номера для символов, в данном случае 0x0041 и 0x03B1 соответственно. Эти уникальные номера называются кодовыми точками. Кодовая точка — это в основном позиция символа в наборе всех возможных символов Unicode, и поэтому в Perl термин порядковый номер часто используется как синоним.

Стандарт Unicode предпочитает использовать шестнадцатеричную нотацию для кодовых точек. Если числа, такие как 0x0041, вам незнакомы, ознакомьтесь с более поздним разделом "Шестнадцатеричная нотация". Стандарт Unicode использует запись U+0041 LATIN CAPITAL LETTER A, чтобы указать шестнадцатеричную кодовую точку и нормативное имя символа.

Unicode также определяет различные свойства символов, такие как «заглавный» или «строчный», «десятичная цифра» или «знак препинания»; эти свойства независимы от имен символов. Кроме того, определены различные операции над символами, такие как преобразование в верхний и нижний регистр и сортировка (сортировка).

Unicode логический «символ» может на самом деле состоять из более чем одного внутреннего фактического «символа» или кодовой точки. Для западноевропейских языков это адекватно моделируется основным символом (например, LATIN CAPITAL LETTER A) и одним или несколькими модификаторами (например, COMBINING ACUTE ACCENT). Эта последовательность основного символа и модификаторов называется последовательностью комбинируемых символов. Некоторые неевропейские языки требуют более сложных моделей, поэтому Unicode создал концепцию кластера графем, которая позже была дополнительно уточнена в расширенный кластер графем. Например, корейский слог хангуль считается одним логическим символом, но чаще всего состоит из трёх фактических символов Unicode: ведущей согласной, внутренней гласной и последующей согласной.

Называть ли эти расширенные кластеры графем «символами», зависит от вашей точки зрения. Если вы программист, вы, вероятно, будете склонны рассматривать каждый элемент в последовательностях как одну единицу или «символ». Однако с точки зрения пользователя вся последовательность может быть воспринята как один «символ», так как именно так это обычно выглядит в контексте языка пользователя. В этом документе мы придерживаемся точки зрения программиста: один «символ» — одна кодовая точка Unicode.

Для некоторых комбинаций основного символа и модификаторов существуют прекомпонованные символы. Например, для последовательности LATIN CAPITAL LETTER A и COMBINING ACUTE ACCENT есть эквивалентный единичный символ — LATIN CAPITAL LETTER A WITH ACUTE. Однако такие прекомпонованные символы доступны только для некоторых комбинаций и предназначены в основном для поддержки обратных преобразований между Unicode и старыми стандартами (например, ISO 8859). Использование последовательностей, как в Unicode, позволяет использовать меньше основных строительных блоков (кодовых точек) для выражения множества возможных кластеров графем. Для поддержки преобразования между эквивалентными формами также определены различные формы нормализации. Таким образом, LATIN CAPITAL LETTER A WITH ACUTE находится в составной форме нормализации (сокращённо NFC), а последовательность LATIN CAPITAL LETTER A и COMBINING ACUTE ACCENT представляет тот же символ в разложенной форме нормализации (NFD).

Из-за обратной совместимости со старыми кодировками идея «уникального номера для каждого символа» немного нарушается: вместо этого есть «по крайней мере один номер для каждого символа». Один и тот же символ может быть представлен по-разному в нескольких старых кодировках. Обратное неверно: некоторые кодовые точки не имеют назначенного символа. Во-первых, существуют неназначенные кодовые точки в используемых блоках. Во-вторых, существуют специальные управляющие символы Unicode, которые не представляют собой настоящие символы.

Когда Unicode был впервые задуман, считалось, что все символы мира могут быть представлены с помощью 16-битного слова; то есть максимум 0x10000 (или 65 536) символов будет необходимо, от 0x0000 до 0xFFFF. Это вскоре оказалось неверным, и начиная с Unicode 2.0 (июль 1996 года), Unicode был определен вплоть до 21 бита (0x10FFFF), а Unicode 3.1 (март 2001 года) определил первые символы, превышающие 0xFFFF. Первые 0x10000 символы называются плоскостью 0 или базовой многоязыковой плоскостью (BMP). С Unicode 3.1 было определено 17 (да, семнадцать) плоскостей — но они ещё далеки от заполнения определёнными символами.

Когда кодируется новый язык, Unicode обычно выбирает block последовательных неназначенных кодовых точек для своих символов. До сих пор количество кодовых точек в этих блоках всегда было кратно 16. Избыточные кодовые точки в блоке, которые в настоящее время не нужны, оставляются неназначенными для будущего роста. Но были случаи, когда в более поздней версии потребовалось больше кодовых точек, чем имелось избыточных, и новый блок пришлось разместить в другом месте, не смежном с исходным, для обработки переполнения. Таким образом, довольно рано стало очевидно, что «блок» не является подходящим принципом организации, и поэтому было создано свойство Script. (Позже было добавлено улучшенное свойство скрипта — Script_Extensions. Кодовые точки в блоках переполнения всё ещё могут иметь тот же скрипт, что и исходные.) Концепция скрипта лучше соответствует естественному языку: есть Latin скрипт, Greek скрипт и так далее; и существуют несколько искусственных скриптов, например Common для символов, используемых в нескольких скриптах, таких как математические символы. Скрипты обычно охватывают различные части нескольких блоков. Для получения более подробной информации о скриптах см. "Скрипты" в perlunicode. Разделение на блоки существует, но оно почти полностью случайное — артефакт того, как символы были и продолжают распределяться. (Обратите внимание, что этот абзац упростил некоторые вещи ради введения. Unicode фактически не кодирует языки, а кодирует системы письма для них — их скрипты; и один скрипт может использоваться многими языками. Unicode также кодирует вещи, которые не так сильно связаны с языками, например, символы, такие как BAGGAGE CLAIM.)

Кодовые точки Unicode — это просто абстрактные числа. Для ввода и вывода этих абстрактных чисел числа должны быть закодированы или сериализованы каким-либо образом. Unicode определяет несколько форм кодирования символов, из которых UTF-8 является самой популярной. UTF-8 — это кодирование с переменной длиной, которое кодирует символы Unicode как от 1 до 4 байтов. Другие кодировки включают UTF-16 и UTF-32 и их варианты с порядком байтов (UTF-8 независим от порядка байтов). ISO/IEC 10646 определяет формы кодирования UCS-2 и UCS-4.

Для получения более подробной информации о кодировках — например, чтобы узнать, что такое супплементы и метки порядка байтов (BOM) — см. perlunicode.

Поддержка Unicode в Perl

Начиная с Perl v5.6.0, Perl имеет возможность обрабатывать Unicode напрямую. Однако Perl v5.8.0 — это первая рекомендуемая версия для серьёзной работы с Unicode. Внесение в 5.6.1 исправлений к первоначальной реализации Unicode устранило много проблем, но, например, регулярные выражения всё ещё не работают с Unicode в 5.6.1. Perl v5.14.0 — это первая версия, где поддержка Unicode практически без проблем интегрируется без особых проблем. (Есть несколько исключений. Во-первых, некоторые отличия в quotemeta были исправлены, начиная с Perl 5.16.0. Во-вторых, некоторые отличия в операторе диапазона были исправлены, начиная с Perl 5.26.0. В-третьих, некоторые отличия в split были исправлены начиная с Perl 5.28.0.)

Чтобы обеспечить эту беспроблемную поддержку, вы должны use feature 'unicode_strings' (что автоматически выбирается, если вы используете use v5.12 или выше). См. feature. (5.14 также исправляет ряд ошибок и отклонений от стандарта Unicode.)

Перед Perl v5.8.0 использовалось use utf8, чтобы объявить, что операции в текущем блоке или файле будут работать с Unicode. Эта модель оказалась неверной, или по крайней мере неуклюжей: «Unicode-ность» теперь передаётся вместе с данными, а не прикрепляется к операциям. Начиная с Perl v5.8.0, осталось только одно место, где требуется явное use utf8: если ваш скрипт Perl закодирован в UTF-8, вы можете использовать UTF-8 в именах идентификаторов, а также в строковых и регулярных выражениях, указав use utf8. Это не значение по умолчанию, потому что скрипты со старыми 8-битными данными в них сломаются. См. utf8.

Модель Unicode в Perl

Perl поддерживает как строки в формате 8-битных собственных байтов (до версии 5.6), так и строки с символами Unicode. Общий принцип заключается в том, что Perl пытается сохранить данные в виде 8-битных байтов как можно дольше, но как только Unicode-ность не может быть избегнута, данные прозрачно обновляются до Unicode. До Perl v5.14.0 обновление не было полностью прозрачным (см. "Ошибка Unicode" в perlunicode), и для обратной совместимости полная прозрачность достигается только тогда, когда выбрано use feature 'unicode_strings' (см. feature) или use v5.12 (или выше).

Внутренне Perl в настоящее время использует либо набор символов платформы с 8-битными нативными символами (например, Latin-1), по умолчанию UTF-8, для кодирования строк Unicode. Конкретно, если все кодовые точки в строке не превышают 0xFF, Perl использует набор символов платформы с 8-битными нативными символами. В противном случае используется UTF-8.

Пользователю Perl обычно не нужно знать и не нужно заботиться о том, как Perl кодирует свои внутренние строки, но это становится актуальным при выводе строк Unicode в поток без уровня PerlIO (с кодировкой «по умолчанию»). В этом случае будут использованы внутренние сырые байты (набор символов платформы или UTF-8, в зависимости от каждой строки), и будет выдано предупреждение «Символ большой ширины», если эти строки содержат символ с кодом выше 0x00FF.

Например,

perl -e 'print "\x{DF}\n", "\x{0100}\x{DF}\n"'

производит довольно бесполезную смесь нативных байтов и UTF-8, а также предупреждение:

Wide character in print at ...

Для вывода UTF-8 используйте уровень вывода :encoding или :utf8. Добавление

binmode(STDOUT, ":utf8");

к этой программе гарантирует, что вывод полностью в UTF-8, и устраняет предупреждение программы.

Вы можете включить автоматическое преобразование ваших стандартных файлов в UTF-8, кодировку по умолчанию open() и @ARGV с помощью переключателя командной строки -C или переменной среды PERL_UNICODE, см. perlrun для документации переключателя -C.

Обратите внимание, что это означает, что Perl ожидает, что другое программное обеспечение будет работать таким же образом: если Perl считает, что STDIN должен быть UTF-8, но STDIN из другого командного вызова не UTF-8, Perl, вероятно, пожалуется на неверный UTF-8.

Все функции, которые сочетают Unicode и ввод-вывод, также требуют использования новой функции PerlIO. Однако почти все платформы Perl 5.8 используют PerlIO: вы можете узнать, использует ли ваша, выполнив "perl -V" и найдя useperlio=define.

Unicode и EBCDIC

Perl 5.8.0 добавила поддержку Unicode на платформах EBCDIC. Эта поддержка была прекращена в более поздних версиях, но была восстановлена в версии 5.22. Поддержка Unicode несколько сложнее для реализации, так как требуются дополнительные преобразования. Дополнительную информацию см. в perlebcdic.

На платформах EBCDIC внутренняя форма кодирования Unicode — UTF-EBCDIC вместо UTF-8. Разница в том, что UTF-8 «безопасен для ASCII» в том, что символы ASCII кодируются в UTF-8 как есть, в то время как UTF-EBCDIC «безопасен для EBCDIC», так что все основные символы (включая все те, которые имеют эквиваленты ASCII (например, "A", "0", "%", и т.д.)) одинаковы как в EBCDIC, так и в UTF-EBCDIC. Часто в документации термин «UTF-8» используется и для обозначения UTF-EBCDIC. Это относится и к данному документу.

Создание Unicode

Этот раздел полностью относится к Perl, начиная с версии v5.22. Различные оговорки для более ранних выпусков приведены в подразделе "Оговорки для более ранних версий" ниже.

Для создания символов Unicode в литералах используйте запись \N{...} в строках с двойными кавычками:

my $smiley_from_name = "\N{WHITE SMILING FACE}";
my $smiley_from_code_point = "\N{U+263a}";

Аналогично, они могут быть использованы в литералах регулярных выражений

$smiley =~ /\N{WHITE SMILING FACE}/;
$smiley =~ /\N{U+263a}/;

или, начиная с версии v5.32:

$smiley =~ /\p{Name=WHITE SMILING FACE}/;
$smiley =~ /\p{Name=whitesmilingface}/;

Во время выполнения вы можете использовать:

use charnames ();
my $hebrew_alef_from_name
                     = charnames::string_vianame("HEBREW LETTER ALEF");
my $hebrew_alef_from_code_point = charnames::string_vianame("U+05D0");

Естественно, ord() выполнит обратное действие: преобразует символ в кодовую точку.

Существуют и другие варианты во время выполнения. Вы можете использовать pack().

my $hebrew_alef_from_code_point = pack("U", 0x05d0);

Или вы можете использовать chr(), хотя в общем случае это менее удобно:

$hebrew_alef_from_code_point = chr(utf8::unicode_to_native(0x05d0));
utf8::upgrade($hebrew_alef_from_code_point);

utf8::unicode_to_native() и utf8::upgrade() не нужны, если аргумент выше 0xFF, поэтому вышеприведённое можно было записать как

$hebrew_alef_from_code_point = chr(0x05d0);

поскольку 0x5d0 больше 255.

\x{} и \o{} также могут использоваться для указания кодовых точек во время компиляции в строках с двойными кавычками, но, для обратной совместимости со старыми версиями Perl, применяются те же правила, что и с chr() для кодовых точек меньше 256.

utf8::unicode_to_native() используется для того, чтобы код Perl был портативным для платформ EBCDIC. Вы можете опустить его, если вы абсолютно уверены, что никто никогда не захочет использовать ваш код на платформе, отличной от ASCII. Начиная с Perl v5.22, вызовы к нему на платформах ASCII оптимизируются, поэтому добавление его не влияет на производительность. Либо вы можете просто использовать другие конструкции, для которых это не требуется.

См. "Дополнительные ресурсы", чтобы найти все эти имена и числовые коды.

Оговорки для более ранних версий

На платформах EBCDIC до v5.22 использование \N{U+...} не работает должным образом.

До версии v5.16 использование \N{...} с именем символа (в отличие от кодовой точки U+...) требовало use charnames :full.

До версии v5.14 были ошибки в \N{...} с именем символа (в отличие от кодовой точки U+...).

charnames::string_vianame() был введён в v5.14. До этого charnames::vianame() должен был работать, но только если аргумент был в формате "U+...". Вариант для получения Unicode по имени символа во время выполнения, вероятно, следующий:

use charnames ();
my $hebrew_alef_from_name
                 = pack("U", charnames::vianame("HEBREW LETTER ALEF"));

Обработка Unicode

Обработка Unicode в большинстве случаев прозрачна: просто используйте строки как обычно. Функции, такие как index(), length(), и substr(), будут работать с символами Unicode; регулярные выражения будут работать с символами Unicode (см. perlunicode и perlretut).

Обратите внимание, что Perl рассматривает кластеры графем как отдельные символы, поэтому, например,

print length("\N{LATIN CAPITAL LETTER A}\N{COMBINING ACUTE ACCENT}"),
      "\n";

выведет 2, а не 1. Единственное исключение состоит в том, что регулярные выражения имеют \X для сопоставления расширенного кластера графем. (Таким образом, \X в регулярном выражении будет сопоставлять всю последовательность обоих символов примера.)

Однако жизнь не совсем прозрачна при работе со старыми кодировками, вводом-выводом и определёнными особыми случаями:

Старые кодировки

При сочетании старых данных и Unicode старые данные должны быть обновлены до Unicode. Обычно предполагается, что старые данные закодированы в ISO 8859-1 (или EBCDIC, если применимо).

Модуль Encode знает о многих кодировках и имеет интерфейсы для преобразования между этими кодировками:

use Encode 'decode';
$data = decode("iso-8859-3", $data); # convert from legacy

Ввод-вывод Unicode

Обычно вывод данных Unicode

print FH $some_string_with_unicode, "\n";

генерирует сырые байты, которые Perl использует для внутреннего кодирования строки Unicode. Внутреннее кодирование Perl зависит от системы, а также от символов в строке в данный момент. Если какие-либо символы имеют кодовые точки 0x100 или выше, вы получите предупреждение. Чтобы обеспечить, что вывод явно отображается в желаемой кодировке, и избежать предупреждения, откройте поток с желаемой кодировкой. Некоторые примеры:

open FH, ">:utf8", "file";

open FH, ">:encoding(ucs2)",      "file";
open FH, ">:encoding(UTF-8)",     "file";
open FH, ">:encoding(shift_jis)", "file";

и для уже открытых потоков используйте binmode().

binmode(STDOUT, ":utf8");

binmode(STDOUT, ":encoding(ucs2)");
binmode(STDOUT, ":encoding(UTF-8)");
binmode(STDOUT, ":encoding(shift_jis)");

Сопоставление имён кодировок допускает некоторую свободу: регистр не важен, и многие кодировки имеют несколько псевдонимов. Обратите внимание, что слой :utf8 всегда должен быть указан точно так; он не подлежит свободному сопоставлению имён кодировок. Также обратите внимание, что в настоящее время :utf8 небезопасен для ввода, так как он принимает данные без проверки того, является ли он действительным UTF-8; вместо этого вы должны использовать :encoding(UTF-8) (с дефисом или без).

См. PerlIO для слоя :utf8, PerlIO::encoding и Encode::PerlIO для слоя :encoding(), и Encode::Supported для множества кодировок, поддерживаемых модулем Encode.

Чтение файла, который вы знаете, закодирован в одной из Unicode или старых кодировок, не превращает данные в Unicode в глазах Perl. Для этого укажите соответствующий уровень при открытии файлов

open(my $fh,'<:encoding(UTF-8)', 'anything');
my $line_of_unicode = <$fh>;

open(my $fh,'<:encoding(Big5)', 'anything');
my $line_of_unicode = <$fh>;

Уровни ввода-вывода также можно задать более гибко с помощью оператора open. См. open или посмотрите на следующий пример.

use open ':encoding(UTF-8)'; # input/output default encoding will be
                             # UTF-8
open X, ">file";
print X chr(0x100), "\n";
close X;
open Y, "<file";
printf "%#x\n", ord(<Y>); # this should print 0x100
close Y;

С помощью оператора open вы можете использовать слой :locale.

BEGIN { $ENV{LC_ALL} = $ENV{LANG} = 'ru_RU.KOI8-R' }
# the :locale will probe the locale environment variables like
# LC_ALL
use open OUT => ':locale'; # russki parusski
open(O, ">koi8");
print O chr(0x430); # Unicode CYRILLIC SMALL LETTER A = KOI8-R 0xc1
close O;
open(I, "<koi8");
printf "%#x\n", ord(<I>), "\n"; # this should print 0xc1
close I;

Эти методы устанавливают прозрачный фильтр на потоке ввода-вывода, который преобразует данные из указанной кодировки при чтении из потока. Результат всегда Unicode.

Оператор open влияет на все вызовы open() после оператора, задавая уровни по умолчанию. Если вы хотите повлиять только на определённые потоки, используйте явные уровни непосредственно в вызове open().

Вы можете переключать кодировки в уже открытом потоке, используя binmode(); см. "binmode" в perlfunc.

:locale в настоящее время не работает с open() и binmode(), только с оператором open. Методы :utf8 и :encoding(...) работают со всеми open(), binmode(), и оператором open.

Аналогичным образом, вы можете использовать эти уровни ввода-вывода на потоках вывода для автоматического преобразования Unicode в указанную кодировку при записи в поток. Например, следующий фрагмент копирует содержимое файла «text.jis» (кодировка ISO-2022-JP, или JIS) в файл «text.utf8», закодированный в UTF-8:

open(my $nihongo, '<:encoding(iso-2022-jp)', 'text.jis');
open(my $unicode, '>:utf8',                  'text.utf8');
while (<$nihongo>) { print $unicode $_ }

Наименование кодировок, как в операторе open(), так и в операторе open, позволяет использовать гибкие имена: koi8-r и KOI8R будут поняты.

Признаются общие кодировки, признанные ISO, MIME, IANA и различными другими организациями по стандартизации; для более подробного списка см. Encode::Supported.

read() считывает символы и возвращает количество символов. seek() и tell() работают с подсчётом байтов, как и sysseek().

sysread() и syswrite() не следует использовать с файловыми дескрипторами, имеющими слои кодирования символов, так как они ведут себя некорректно, а такое поведение устарело начиная с Perl 5.24.

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

# BAD CODE WARNING
open F, "file";
local $/; ## read in the whole file of 8-bit characters
$t = <F>;
close F;
open F, ">:encoding(UTF-8)", "file";
print F $t; ## convert to UTF-8 on output
close F;

Если запустить этот код дважды, содержимое файла file будет закодировано в UTF-8 дважды. Использование use open ':encoding(UTF-8)' позволило бы избежать ошибки, или явного открытия file также для ввода в формате UTF-8.

ПРИМЕЧАНИЕ: функции :utf8 и :encoding работают только если ваш Perl был скомпилирован с PerlIO, что является по умолчанию на большинстве систем.

Отображение Unicode как текста

Иногда вам может потребоваться отобразить перловские скаляры, содержащие Unicode, как простой ASCII (или EBCDIC) текст. Следующая подпрограмма преобразует свой аргумент так, что символы Unicode с кодовыми точками больше 255 отображаются как \x{...}, управляющие символы (например, \n) отображаются как \x.., а остальные символы — как есть:

sub nice_string {
       join("",
       map { $_ > 255                    # if wide character...
             ? sprintf("\\x{%04X}", $_)  # \x{...}
             : chr($_) =~ /[[:cntrl:]]/  # else if control character...
               ? sprintf("\\x%02X", $_)  # \x..
               : quotemeta(chr($_))      # else quoted or as themselves
       } unpack("W*", $_[0]));           # unpack Unicode characters
  }

Например,

nice_string("foo\x{100}bar\n")

возвращает строку

'foo\x{0100}bar\x0A'

которая готова для печати.

(\\x{} используется здесь вместо \\N{}, так как, скорее всего, вы хотите увидеть исходные значения.)

Особые случаи

  • Начиная с Perl 5.28, битовые операторы, такие как ~, не могут применяться к строкам, содержащим кодовые точки больше 255.

  • Функция vec() может давать неожиданные результаты, если используется со строками, содержащими символы с порядковыми значениями больше 255. В таком случае результаты соответствуют внутреннему кодированию символов, но не многим другим аспектам. Поэтому не делайте этого, и начиная с Perl 5.28, при этом выдается сообщение об устаревании, которое станет ошибкой в Perl 5.32.

  • Просмотр внутреннего кодирования Perl

    Обычные пользователи Perl не должны заботиться о том, как Perl кодирует конкретную строку Unicode (потому что обычные способы получения содержимого строки с Unicode — через вход и выход — всегда должны выполняться через явно определенные слои ввода-вывода). Но если вам необходимо, есть два способа взглянуть за кулисы.

    Один из способов взглянуть во внутреннее кодирование символов Unicode — использовать unpack("C*", ... для получения байтов того или иного кодирования строки, или unpack("U0..", ...) для получения байтов UTF-8 кодирования:

    # this prints  c4 80  for the UTF-8 bytes 0xc4 0x80
    print join(" ", unpack("U0(H2)*", pack("U", 0x100))), "\n";

    Еще один способ — использовать модуль Devel::Peek:

    perl -MDevel::Peek -e 'Dump(chr(0x100))'

    Это показывает флаг UTF8 в FLAGS и и байты UTF-8, и символы Unicode в PV. См. также обсуждение функции utf8::is_utf8() позже в этом документе.

Расширенные темы

  • Равенство строк

    Вопрос о равенстве строк становится несколько сложным в Unicode: что вы подразумеваете под «равенством»?

    (Является ли LATIN CAPITAL LETTER A WITH ACUTE равным LATIN CAPITAL LETTER A?)

    Короткий ответ заключается в том, что по умолчанию Perl сравнивает равенство (eq, ne) только на основе кодовых точек символов. В приведенном выше случае ответ — нет (потому что 0x00C1 != 0x0041). Но иногда любые символы A с заглавной буквы должны считаться равными, или даже A's любого регистра.

    Длинный ответ заключается в том, что вам необходимо учитывать вопросы нормализации и регистров символов: см. Unicode::Normalize, Технический отчет Unicode № 15, Формы нормализации Unicode и разделы о преобразовании регистров в Стандарте Unicode.

    Начиная с Perl 5.8.0, реализовано «полное» преобразование к нижнему регистру Case Mappings/SpecialCasing, но в qr//i с ними остаются ошибки, в основном исправленные в 5.14 и в значительной степени в 5.18.

  • Сортировка строк

    Люди любят видеть свои строки красиво отсортированными — или, как говорится в терминологии Unicode, упорядоченными. Но что вы подразумеваете под сортировкой?

    (Предшествует ли LATIN CAPITAL LETTER A WITH ACUTE или следует за LATIN CAPITAL LETTER A WITH GRAVE?)

    Короткий ответ заключается в том, что по умолчанию Perl сравнивает строки (lt, le, cmp, ge, gt) только на основе кодовых точек символов. В приведенном выше случае ответ — «после», так как 0x00C1 > 0x00C0.

    Длинный ответ заключается в том, что «это зависит», и на него невозможно ответить без знания (по крайней мере) контекста языка. См. Unicode::Collate и Алгоритм сортировки Unicode https://www.unicode.org/reports/tr10/

Случайные

  • Диапазоны и классы символов

    Диапазоны символов в квадратных скобках регулярных выражений (например, /[a-z]/) и операторе tr/// (также известном как y///) не являются волшебным образом знакомыми с Unicode. Это означает, что [A-Za-z] не будет магически означать «все буквы» (не то чтобы даже для 8-битных символов; если вы используете локаль (perllocale), используйте /[[:alpha:]]/; а если нет, используйте 8-битный атрибут \p{alpha}).

    Все свойства, начинающиеся с \p (и его обратного \P) на самом деле являются классами символов, знакомыми с Unicode. Их десятки, см. perluniprops.

    Начиная с версии 5.22, в качестве конечных точек диапазонов символов в шаблонах регулярных выражений можно использовать кодовые точки Unicode, а диапазон будет включать все кодовые точки Unicode, лежащие между этими конечными точками включительно.

    qr/ [ \N{U+03} - \N{U+20} ] /xx

    включает кодовые точки \N{U+03}, \N{U+04}, ..., \N{U+20}.

    Это также работает для диапазонов в tr/// начиная с Perl v5.24.

  • Преобразования строк в числа

    Unicode определяет несколько других десятичных и числовых символов помимо привычных 0–9, таких как арабские и индийские цифры. Perl не поддерживает преобразование строк в числа для цифр, кроме ASCII 0–9 (и ASCII a–f для шестнадцатеричных). Чтобы получить безопасные преобразования из любой строки Unicode, используйте "num()" в Unicode::UCD.

Вопросы с ответами

  • Сломаются ли мои старые скрипты?

    Вероятнее всего, нет. Если вы каким-то образом не генерируете символы Юникода, старое поведение должно сохраниться. Единственное поведение, которое изменилось и которое может начать генерировать Юникод, — это старое поведение chr(), где передача аргумента больше 255 давала символ по модулю 255. chr(300), например, был равен chr(45) или «-» (в ASCII), теперь это большая латинская буква I с краткой чертой.

  • Как заставить мои скрипты работать с Юникодом?

    Вероятно, потребуется мало изменений, так как ничего не меняется до тех пор, пока вы не генерируете данные Юникода. Самое важное — получать входные данные в виде Юникода; об этом см. предыдущую дискуссию об I/O. Для полной бесшовной поддержки Юникода добавьте use feature 'unicode_strings' (или use v5.12 или выше) в свой скрипт.

  • Как узнать, находится ли моя строка в Юникоде?

    Вам, возможно, не нужно беспокоиться об этом. Но вам может понадобиться, если ваш Perl имеет версию до 5.14.0 или вы не указали use feature 'unicode_strings' или use 5.012 (или выше), потому что в противном случае правила для кодовых точек в диапазоне от 128 до 255 отличаются в зависимости от того, находится ли строка в Юникоде или нет. (См. "Когда Юникод не используется" в perlunicode.)

    Чтобы определить, находится ли строка в Юникоде, используйте:

    print utf8::is_utf8($string) ? 1 : 0, "\n";

    Но обратите внимание, что это не означает, что какие-либо символы в строке обязательно закодированы в UTF-8, или что какие-либо символы имеют кодовые точки больше 0xFF (255) или даже 0x80 (128), или что в строке есть какие-либо символы вообще. Все, что делает is_utf8(), — это возвращает значение внутреннего флага «utf8ness», прикрепленного к $string. Если флаг выключен, байты в скаляре интерпретируются как кодировка с одним байтом. Если флаг включен, байты в скаляре интерпретируются как кодовые точки символов, закодированные в UTF-8 (переменной длины, потенциально многобайтные). Байты, добавленные в строку UTF-8, автоматически обновляются до UTF-8. Если объединяются смешанные скаляры не-UTF-8 и UTF-8 (интерполяция в двойных кавычках, явное конкатенация или подстановка параметров printf/sprintf), результат будет закодирован в UTF-8 так, как если бы копии байтовых строк были обновлены до UTF-8: например,

    $a = "ab\x80c";
    $b = "\x{100}";
    print "$a = $b\n";

    вывод будет закодирован в UTF-8 ab\x80c = \x{100}\n, но $a останется байтово закодированным.

    Иногда вам может потребоваться узнать длину строки в байтах, а не в символах. Для этого используйте pragma bytes и функцию length():

    my $unicode = chr(0x100);
    print length($unicode), "\n"; # will print 1
    use bytes;
    print length($unicode), "\n"; # will print 2
                                  # (the 0xC4 0x80 of the UTF-8)
    no bytes;
  • Как определить кодировку файла?

    Вы можете попробовать Encode::Guess, но у него есть ряд ограничений.

  • Как обнаружить данные, которые не являются допустимыми в определённой кодировке?

    Используйте пакет Encode, чтобы попытаться преобразовать их. Например,

    use Encode 'decode';
    
    if (eval { decode('UTF-8', $string, Encode::FB_CROAK); 1 }) {
        # $string is valid UTF-8
    } else {
        # $string is not valid UTF-8
    }

    Или используйте unpack, чтобы попытаться декодировать их:

    use warnings;
    @chars = unpack("C0U*", $string_of_bytes_that_I_think_is_utf8);

    Если данные неверны, генерируется предупреждение Malformed UTF-8 character. «C0» означает «обрабатывать строку символ за символом». Без этого unpack("U*", ...) будет работать в режиме U0 (по умолчанию, если строка формата начинается с U) и вернёт байты, составляющие UTF-8-кодировку целевой строки, что всегда будет работать.

  • Как преобразовать двоичные данные в определённую кодировку или наоборот?

    Это, вероятно, не так полезно, как вы думаете. Обычно вам этого не нужно.

    В некотором смысле, то, что вы спрашиваете, не имеет большого смысла: кодировки предназначены для символов, а двоичные данные — не «символы», поэтому преобразование «данных» в какую-либо кодировку не имеет смысла, если вы не знаете, в какой кодовой таблице и кодировке находятся двоичные данные, в этом случае это не просто двоичные данные, так ли это?

    Если у вас есть последовательность байтов, которую вы знаете, должна быть интерпретирована с использованием определённой кодировки, вы можете использовать Encode:

    use Encode 'from_to';
    from_to($data, "iso-8859-1", "UTF-8"); # from latin-1 to UTF-8

    Вызов from_to() изменяет байты в $data, но ничто существенное в природе строки не изменилось с точки зрения Perl. Как до, так и после вызова строка $data содержит только набор 8-битных байтов. С точки зрения Perl, кодировка строки остаётся «системно-родными 8-битными байтами».

    Вы можете сравнить это с воображаемым модулем «Translate»:

    use Translate;
    my $phrase = "Yes";
    Translate::from_to($phrase, 'english', 'deutsch');
    ## phrase now contains "Ja"

    Содержание строки меняется, но не её природа. Perl не узнаёт ничего нового после вызова, кроме того, что содержимое строки указывает на утверждение.

    Возвращаясь к преобразованию данных. Если у вас есть (или нужны) данные в системной кодировке в 8-битных байтах (например, Latin-1, EBCDIC и т. д.), вы можете использовать pack/unpack для преобразования в/из Юникода.

    $native_string  = pack("W*", unpack("U*", $Unicode_string));
    $Unicode_string = pack("U*", unpack("W*", $native_string));

    Если у вас есть последовательность байтов, вы знаете, что она является валидным UTF-8, но Perl ещё не знает об этом, вы можете убедить Perl в этом тоже:

    $Unicode = $bytes;
    utf8::decode($Unicode);

    или:

    $Unicode = pack("U0a*", $bytes);

    Вы можете найти байты, составляющие последовательность UTF-8:

    @bytes = unpack("C*", $Unicode_string)

    и вы можете создать хорошо сформированный Юникод:

    $Unicode_string = pack("U*", 0xff, ...)
  • Как отобразить Юникод? Как ввести Юникод?

    См. http://www.alanwood.net/unicode/ и http://www.cl.cam.ac.uk/~mgk25/unicode.html

  • Как Юникод взаимодействует с традиционными локализациями?

    Если ваша локаль — UTF-8, начиная с Perl v5.26, Perl хорошо работает со всеми категориями; до этого, начиная с Perl v5.20, он работает со всеми категориями, кроме LC_COLLATE, который связан с сортировкой и оператором cmp. Обратите внимание, что стандартные модули Unicode::Collate и Unicode::Collate::Locale предлагают более мощные решения проблем сортировки и работают в более ранних версиях.

    Для других локалей, начиная с Perl 5.16, вы можете указать

    use locale ':not_characters';

    чтобы Perl хорошо работал с ними. Проблема в том, что вам нужно самостоятельно преобразовывать символы из кодовой таблицы локали в/из Юникода. Смотрите "Юникод I/O" выше, как это сделать

    use open ':locale';

    чтобы достичь этого, но полные подробности приведены в "Юникод и UTF-8" в perllocale, включая подвохи, возникающие, если вы не указываете :not_characters.

Шестнадцатеричная нотация

Стандарт Юникода предпочитает использовать шестнадцатеричную нотацию, потому что она более наглядно демонстрирует разделение Юникода на блоки из 256 символов. Шестнадцатеричная нотация также короче десятичной. Вы также можете использовать десятичную нотацию, но изучение шестнадцатеричной нотации просто облегчает работу со стандартом Юникода. Например, используется нотация U+HHHH.

Префикс 0x означает шестнадцатеричное число, цифры — 0-9 и a-f (или A-F, регистр не имеет значения). Каждая шестнадцатеричная цифра представляет четыре бита или половину байта. print 0x..., "\n" покажет шестнадцатеричное число в десятичной форме, а printf "%x\n", $decimal покажет десятичное число в шестнадцатеричной форме. Если у вас есть только «шестнадцатеричные цифры» шестнадцатеричного числа, вы можете использовать функцию hex().

print 0x0009, "\n";    # 9
print 0x000a, "\n";    # 10
print 0x000f, "\n";    # 15
print 0x0010, "\n";    # 16
print 0x0011, "\n";    # 17
print 0x0100, "\n";    # 256

print 0x0041, "\n";    # 65

printf "%x\n",  65;    # 41
printf "%#x\n", 65;    # 0x41

print hex("41"), "\n"; # 65

Дополнительные ресурсы

  • Консорциум Юникода

    https://www.unicode.org/

  • Вопросы и ответы о Юникоде

    https://www.unicode.org/faq/

  • Глоссарий Юникода

    https://www.unicode.org/glossary/

  • Рекомендованный список литературы по Юникоду

    Консорциум Юникода предоставляет список статей и книг, некоторые из которых предоставляют более глубокое рассмотрение Юникода: http://unicode.org/resources/readinglist.html

  • Полезные ресурсы по Юникоду

    https://www.unicode.org/unicode/onlinedat/resources.html

  • Юникод и поддержка нескольких языков в HTML, шрифтах, веб-браузерах и других приложениях

    http://www.alanwood.net/unicode/

  • Вопросы и ответы о UTF-8 и Юникоде для Unix/Linux

    http://www.cl.cam.ac.uk/~mgk25/unicode.html

  • Кодовые таблицы старого образца

    http://www.czyborra.com/ http://www.eki.ee/letter/

  • Вы можете изучить различные сведения из файлов данных Юникода, используя модуль Unicode::UCD.

ЮНИКОД В СТАРЫХ ВЕРСИЯХ PERL

Если вы не можете обновить Perl до версии 5.8.0 или выше, вы все равно можете выполнить некоторые операции с Юникодом, используя модули Unicode::String, Unicode::Map8, и Unicode::Map, доступные из CPAN. Если у вас установлен GNU recode, вы также можете использовать Perl-интерфейс Convert::Recode для преобразования символов.

Ниже приведены быстрые преобразования из байтов ISO 8859-1 (Latin-1) в байты UTF-8 и обратно. Код работает даже со старыми версиями Perl 5.

# ISO 8859-1 to UTF-8
s/([\x80-\xFF])/chr(0xC0|ord($1)>>6).chr(0x80|ord($1)&0x3F)/eg;

# UTF-8 to ISO 8859-1
s/([\xC2\xC3])([\x80-\xBF])/chr(ord($1)<<6&0xC0|ord($2)&0x3F)/eg;

См. также

perlunitut, perlunicode, Encode, open, utf8, bytes, perlretut, perlrun, Unicode::Collate, Unicode::Normalize, Unicode::UCD

ПРИЗНАТЕЛЬНОСТИ

Благодарим читателей рассылок perl5-porters@perl.org, perl-unicode@perl.org, linux-utf8@nl.linux.org и unicore@unicode.org за ценные отзывы.

Автор, авторские права и лицензия

Авторские права 2001-2011 Jarkko Hietaniemi <jhi@iki.fi>. В настоящее время поддерживается Perl 5 Porters.

Данный документ может быть распространён на тех же условиях, что и Perl.

© 1993–2023 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.38.0/perluniintro

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API