Spec-Zone.ru › Perl 5.36

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 и legacy стандартами (такими как 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 и их варианты с порядком байтов big- и little-endian (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-битных кодировок по умолчанию, так и строки Unicode-символов. Общий принцип заключается в том, что Perl старается сохранять данные в виде 8-битных байтов как можно дольше, но как только Unicodeness нельзя избежать, данные прозрачно преобразуются в 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: вы можете проверить, использует ли ваша платформа 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, начиная с версии 5.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}/;

или, начиная с версии 5.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 до версии 5.22 использование \N{U+...} не работает должным образом.

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

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

charnames::string_vianame() был введён в версии 5.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().

END_OF_DOCUMENT_MARKER

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;

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

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

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

Иногда вам может понадобиться отобразить перл-скаляры, содержащие Юникод, как простой ASCII (или EBCDIC) текст. Следующая подпрограмма преобразует ее аргумент таким образом, что символы Юникода с кодовыми точками больше 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 кодирует конкретные строки Юникод (потому что стандартные способы доступа к содержимому строки с Юникодом — через ввод и вывод — всегда должны осуществляться с помощью явных слоев ввода-вывода). Но если вам нужно, есть два способа взглянуть на внутренности.

    Один способ просмотреть внутреннюю кодировку символов Юникода — использовать 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 в флагах и как байты UTF-8, так и символы Юникода в PV. Также см. далее в этом документе обсуждение функции utf8::is_utf8().

Дополнительные темы

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

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

    (Равна ли LATIN CAPITAL LETTER A WITH ACUTE строке LATIN CAPITAL LETTER A?)

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

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

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

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

    Пользователи хотят видеть свои строки в виде отсортированного списка — или, говоря языком Юникода, сортированного. Но что вы имеете в виду под сортировкой?

    (Идёт ли 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///) не обладают магическими свойствами Юникода. Это означает, что [A-Za-z] не станет магически означать «все буквы». (Не то чтобы это означало и для 8-битных символов. Для них, если вы используете локали (perllocale), используйте /[[:alpha:]]/; а если нет, используйте 8-битную характеристику \p{alpha}).

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

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

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

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

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

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

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

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

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

    Вероятно, нет. Если вы каким-то образом не генерируете символы Юникода, старое поведение должно сохраниться. Практически единственное изменившееся поведение, которое может начать генерировать Юникод, — это старое поведение chr(), где передача аргумента больше 255 давала символ по модулю 255. chr(300), например, был равен chr(45) или «-» (в ASCII), теперь это ЗАГЛАВНАЯ ЛАТИНСКАЯ БУКВА I С ДИАРЕЗИСОМ.

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

    Вероятно, понадобится очень немного работы, так как ничего не меняется, пока вы не генерируете данные Юникода. Самое важное — получать входные данные в виде Юникода; об этом см. обсуждение ввода-вывода выше. Чтобы получить полную бесшовную поддержку Юникода, добавьте 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 останется закодированной в байтах.

    Иногда вам может понадобиться узнать длину строки в байтах, а не в символах. Для этого используйте псевдоним 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 хорошо с ними работал. Загвоздка в том, что вам нужно самим преобразовывать символы из/в набор символов локали в Юникод. См. "Unicode ввод/вывод" выше, чтобы

    use open ':locale';

    чтобы сделать это, но подробная информация находится в "Unicode и 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–2021 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.36.0/perluniintro

Spec-Zone.ru

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