Spec-Zone.ru › Perl 5.28

perlunicode

СОДЕРЖАНИЕ

  • ИМЯ
  • ОПИСАНИЕ
    • Важные замечания
    • Семантика байтов и символов
    • Правила ASCII против правил Unicode
    • Расширенные кластеры графем (логические символы)
    • Свойства символов Unicode
      • Категория
      • Типы символов с двунаправленной обработкой
      • Скрипты
      • Использование префикса "Is"
      • Блоки
      • Другие свойства
    • Пользовательские свойства символов
    • Пользовательские сопоставления регистров (только для серьезных хакеров)
    • Кодировки символов для ввода и вывода
    • Уровень поддержки Unicode для регулярных выражений
      • Уровень 1 — Базовая поддержка Unicode
      • Уровень 2 — Расширенная поддержка Unicode
      • Уровень 3 — Настроенная поддержка
    • Кодировки Unicode
    • Кодовые точки, не являющиеся символами
    • За пределами кодовых точек Unicode
    • Последствия использования Unicode для безопасности
    • Unicode в Perl на EBCDIC
    • Локали
    • Когда Unicode не используется
    • Ошибка "Unicode"
    • Принудительное использование Unicode в Perl (или отказ от использования Unicode в Perl)
    • Использование Unicode в XS
    • Модификация Perl для работы с более ранними версиями Unicode (только для очень серьезных хакеров)
    • Портирование кода из perl-5.6.X
  • ОШИБКИ
    • Взаимодействие с расширениями
    • Скорость
  • СМОТРИТЕ ТАКЖЕ

ИМЯ

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

ОПИСАНИЕ

Если вы еще этого не сделали, перед чтением этого документа вам следует ознакомиться с perlunitut и perluniintro.

Unicode призван UNIфицировать кодировки всех наборов символов мира в единый стандарт. Для многих стандартов кодирования, существовавших до создания Unicode, преобразование в Unicode фактически сводилось к добавлению константы к каждой кодовой точке в исходном стандарте, а преобразование обратно — к вычитанию этой же константы. Для ASCII и ISO-8859-1 константа равна 0. Для ISO-8859-5 (кириллица) константа равна 864; для иврита (ISO-8859-8) — 1488; тайского (ISO-8859-11) — 3424 и так далее. Это упрощало преобразования и способствовало принятию Unicode.

И это сработало; в наши дни эти устаревшие стандарты редко используются. Большинство людей используют Unicode.

Unicode — это всеобъемлющий стандарт. Он определяет множество вещей, выходящих за рамки Perl, таких как отображение последовательностей символов. Для получения полной информации обо всех аспектах Unicode см. http://www.unicode.org.

Важные замечания

Хотя некоторые части этого раздела могут быть непонятны при первом чтении, мы считаем важным выделить некоторые из нюансов до более глубокого погружения, так что вот:

Поддержка Unicode — это обширное требование. Хотя Perl не реализует стандарт Unicode или соответствующие технические отчеты от корки до корки, Perl поддерживает многие функции Unicode.

Кроме того, использование Unicode может создать проблемы безопасности, которые не очевидны, см. "Последствия использования Unicode для безопасности".

Безопаснее всего, если вы use feature 'unicode_strings'

Для сохранения обратной совместимости Perl не включает полную внутреннюю поддержку Unicode, если не указана прагма use feature 'unicode_strings'. (Это автоматически выбирается, если вы use 5.012 или выше.) Отсутствие этого может привести к неожиданным результатам. См. "Ошибка "Unicode"" ниже.

Эта прагма не влияет на ввод-вывод. Также она не изменяет внутреннее представление строк, только их интерпретацию. Есть все еще несколько мест, где Unicode не полностью поддерживается, например, в именах файлов.

Слои ввода-вывода

Используйте :encoding(...) слой для чтения и записи в файловые дескрипторы с указанной кодировкой. (См. open.)

Вы должны преобразовать свои скрипты Perl, не использующие ASCII и не использующие UTF-8, в UTF-8.

Модуль encoding устарел с версии perl 5.18, а необходимые для него внутренние компоненты Perl были удалены в версии 5.26.

use utf8 по-прежнему необходимо для включения UTF-8 в скриптах

Если ваш скрипт Perl закодирован в UTF-8, прагма use utf8 должна быть явно включена, чтобы обеспечить распознавание этого (в строковых или регулярных выражениях, или в именах идентификаторов). Это единственный случай, когда явно требуется use utf8. (См. utf8).

Если скрипт Perl начинается с байтов, которые образуют кодировку UTF-8 Unicode BYTE ORDER MARK (BOM, см. "Кодировки Unicode"), эти байты полностью игнорируются.

Скрипты UTF-16 автоматически распознаются

Если скрипт Perl начинается с Unicode BOM (UTF-16LE, UTF16-BE), или если скрипт выглядит как UTF-16 без обозначения маркер, Perl корректно прочитает его как соответствующую кодировку Unicode.

Семантика байтов и символов

До Unicode большинство кодировок использовали 8 бит (один байт) для кодирования каждого символа. Таким образом, символ был байтом, а байт был символом, и могло быть не более 256 возможных символов. «Семантика байтов» в названии этого раздела относится к этому поведению. Не было необходимости различать «байт» и «символ».

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

Perl пришлось изменить внутреннюю реализацию, чтобы разъединить «байты» и «символы». Важно, чтобы вы тоже изменили своё понимание, если ещё не сделали, чтобы «байт» и «символ» больше не означали одно и то же.

Основным строительным блоком строк Perl всегда был «символ». Изменения в основном сводятся к тому, что реализация больше не считает, что символ всегда является всего лишь одним байтом.

Есть несколько моментов, на которые следует обратить внимание:

  • Функции обработки строк в основном продолжают работать с символами. Например, length(), как и прежде, возвращает количество символов в строке. Но это количество больше не обязательно совпадает с количеством байтов в строке (байтов может быть больше, чем символов). К другим таким функциям относятся chop(), chomp(), substr(), pos(), index(), rindex(), sort(), sprintf(), и write().

    Исключениями являются:

    • битовые операции vec

    • байтовые операции pack/unpack формат "C"

      Однако спецификатор W работает с целыми символами, как и спецификатор U.

    • некоторые операторы, взаимодействующие с операционной системой платформы

      Примеры — операторы, работающие с именами файлов.

    • когда функции вызываются из области действия директивы use bytes

      Скорее всего, вам следует использовать это только для отладки.

  • Строки — включая ключи хэшей — и шаблоны регулярных выражений могут содержать символы с порядковыми значениями, большими, чем 255.

    Если вы используете редактор Unicode для редактирования вашей программы, символы Unicode могут встречаться непосредственно в строковых литералах в кодировке UTF-8 или UTF-16. (В первом случае требуется use utf8, во втором — BOM.)

    "Создание Unicode" в perluniintro предоставляет другие способы размещения символов, отличных от ASCII, в ваших строках.

  • Функции chr() и ord() работают с целыми символами.

  • Регулярные выражения находят целые символы. Например, "." находит целый символ, а не только один байт.

  • Оператор tr/// выполняет перевод целых символов. (Обратите внимание, что функциональность tr///CU была удалена. Для аналогичной функциональности см. pack('U0', ...) и pack('C0', ...)).

  • scalar reverse() инвертирует порядок символов, а не байтов.

  • Операторы битовых строк & | ^ ~ и (начиная с версии 5.22) &. |. ^. ~. могут работать с битовыми строками, закодированными в UTF-8, но это может привести к неожиданным результатам, если какие-либо из строк содержат символы с кодовыми точками выше 0xFF. Начиная с версии 5.28, наличие таких операндов является ошибкой. В противном случае операция выполняется над копией операнда, не закодированной в UTF-8. Если вы не уверены в кодировке строки, понизьте ее уровень перед использованием любого из этих операторов; вы можете использовать utf8::utf8_downgrade().

В итоге, Perl всегда придерживался "семантики символов", но с появлением Unicode это отличается от "семантики байтов".

Правила ASCII и правила Unicode

До Unicode, когда символ был байтом, Perl знал только о 128 символах, определённых ASCII, с кодовыми точками от 0 до 127 (за исключением случаев использования use locale). Это оставляло кодовые точки с 128 по 255 незанятыми и доступными для использования программой по своему усмотрению. Единственной семантикой, которую они имеют, являются их порядковые номера, и то, что они не входят ни в какие классы символов. Ни один из них не считается совпадающим с \w, но все они совпадают с \W.

Конечно, Unicode присваивает каждой из этих кодовых точек определённое значение (наряду с значениями выше 255). Чтобы сохранить обратную совместимость, Perl использует значения Unicode только тогда, когда есть какие-либо указания на то, что используется Unicode; в противном случае несимволы ASCII обрабатываются так, как будто они не назначены.

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

  • В области действия use utf8

    Если вся программа использует Unicode (обозначено использованием 8-битного формата преобразования Unicode Transformation Format), то все строковые литералы внутри неё должны быть Unicode.

  • В области действия use feature 'unicode_strings'

    Эта директива была создана для того, чтобы вы могли явно указать Perl, что операции, выполняемые в её области действия, должны использовать правила Unicode. Больше операций затрагиваются в новых версиях Perl. См. "Проблема Unicode".

  • В области действия use 5.012 или выше

    Это неявно включает use feature 'unicode_strings'.

  • В области действия use locale 'not_characters' или use locale, и текущий локализация — UTF-8.

    Первый случай определён как подразумевающий обработку Unicode; второй указывает на локаль Unicode, следовательно, интерпретацию всех строк внутри неё как Unicode.

  • Когда строка содержит только символы Unicode

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

  • Когда строка содержит имя кодовой точки Unicode \N{...}

    Конструкция \N{...} явно ссылается на кодовую точку Unicode, даже если она также присутствует в ASCII. Поэтому строка, содержащая её, должна быть Unicode.

  • Когда строка получена из внешнего источника, отмеченного как Unicode

    Командная опция -C может указать, что определённые входные данные в программу являются Unicode, и значения этих данных могут быть прочитаны вашим кодом Perl, см. "${^UNICODE}" в perlvar.

  • Когда строка была преобразована в UTF-8

    Функцию utf8::utf8_upgrade() можно явно использовать для постоянного (пока не вызвана последующая utf8::utf8_downgrade()) принудительного обращения со строкой как с Unicode.

  • Существуют дополнительные методы для шаблонов регулярных выражений

    Шаблон, скомпилированный с модификаторами /u или /a, обрабатывается как Unicode (хотя с /a есть некоторые ограничения). С модификаторами /d и /l есть и другие указания на Unicode; см. "Модификаторы наборов символов" в perlre.

Обратите внимание, что всё вышеперечисленное перекрывается в области действия use bytes; но вам следует использовать эту директиву только для отладки.

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

Когда правила Unicode действуют:

  • Операторы преобразования регистра используют таблицы преобразования регистра Unicode.

    Обратите внимание, что uc(), или \U во вставленных строках, преобразуют в верхний регистр, а ucfirst, или \u во вставленных строках, преобразуют в строчный регистр в языках, которые делают это различие (что эквивалентно верхнему регистру в языках без этого различие).

    Существует модуль CPAN, Unicode::Casing, который позволяет определить собственные отображения, которые будут использоваться в lc(), lcfirst(), uc(), ucfirst(), и fc (или их строковые вставки в двойных кавычках, такие как \U). (До Perl 5.16 эта функциональность частично предоставлялась в ядре Perl, но страдала от ряда непреодолимых недостатков, поэтому вместо этого был написан модуль CPAN).

  • Классы символов в регулярных выражениях соответствуют свойствам символов, указанным в базе данных свойств Unicode.

    \w может быть использован для поиска японской иероглифа, а [[:digit:]] — бенгальской цифры.

  • Имена свойств, скриптов и диапазонов блоков Unicode могут использоваться (как скобочные классы символов) с помощью конструкции \p{} «соответствует свойству» и отрицанием \P{}, «не соответствует свойству».

    См. "Свойства символов Unicode" для получения дополнительной информации.

    Вы можете определить свои собственные свойства символов и использовать их в регулярном выражении с помощью конструкции \p{} или \P{}. См. "Пользовательские свойства символов" для получения дополнительной информации.

Расширенные кластеры графем (логические символы)

Рассмотрим символ, например H. Он может появляться с различными знаками вокруг него, такими как острый акцент, или циркумфлекс, или различные крючки, кружки, стрелки и т. д. над, под, сбоку и т. д. Существует множество вариантов среди языков мира. Количество комбинаций астрономическое, и если бы для каждой комбинации был символ, то вскоре исчерпался бы миллион возможных символов Unicode. Поэтому Unicode выбрал другой подход: существует символ для базового H, и символ для каждого из возможных знаков, и эти символы могут быть по-разному объединены для получения конечного логического символа. Таким образом, логический символ — то, что выглядит как один символ — может быть последовательностью более одного отдельного символа. Стандарт Unicode называет их "расширенными кластерами графем" (что является улучшенной версией почти не используемого "кластера графем"); Perl предоставляет конструкцию регулярного выражения \X для поиска таких последовательностей целиком.

Однако цель Unicode — объединить существующие стандарты и практики набора символов, и несколько существующих стандартов имеют отдельные символы, которые означают то же самое, что и некоторые из этих комбинаций, например, ISO-8859-1, у которого их довольно много. Например, "LATIN CAPITAL LETTER E WITH ACUTE" уже был в этом стандарте, когда появился Unicode. Поэтому Unicode добавил его в свой репертуар в качестве этого отдельного символа. Однако этот символ Unicode считает эквивалентным последовательности, состоящей из символа "LATIN CAPITAL LETTER E" и символа "COMBINING ACUTE ACCENT".

"LATIN CAPITAL LETTER E WITH ACUTE" называется «прекомпонованным» символом, а его эквивалентность последовательности «E» и «КОМБИНИРУЮЩИЙ ДИАКРИТИЧЕСКИЙ ЗНАК» называется каноническим эквивалентом. Все прекомпонованные символы, как говорят, имеют разложение (на эквивалентную последовательность), а тип разложения также называется каноническим. Строка может быть составлена по возможности из прекомпонованных символов или целиком из разложенных символов. Юникод называет их соответственно «Нормализованная форма составленная» (NFC) и «Нормализованная форма разложенная». Модуль Unicode::Normalize содержит функции, которые преобразуют между ними. Строка также может содержать как составные, так и разложенные символы; этот модуль можно использовать, чтобы сделать их все одним или другим.

Вы можете столкнуться со строками в любой из этих эквивалентных форм. В настоящее время в Perl 5 нет ничего, что игнорировало бы различия. Поэтому вам нужно будет специально обработать это. Обычно рекомендуется преобразовать ваши входные данные в NFD перед дальнейшей обработкой.

Для получения более подробной информации см. http://unicode.org/reports/tr15/.

Свойства символов Юникода

(Единственный случай, когда Perl рассматривает последовательность отдельных кодовых точек как один логический символ, — это в конструкции \X, уже упомянутой выше. Поэтому «символ» в этой дискуссии означает одну кодовую точку Юникода.)

Практически все свойства символов Юникода доступны через регулярные выражения с использованием конструкции \p{} «соответствует свойству» и \P{} «не соответствует свойству» для отрицания.

Например, \p{Uppercase} соответствует любому одиночному символу со свойством Юникода "Uppercase", в то время как \p{L} соответствует любому символу со свойством General_Category "L" (буква) (см. "General_Category" ниже). Скобки не требуются для имён свойств, состоящих из одной буквы, поэтому \p{L} эквивалентно \pL.

Более формально, \p{Uppercase} соответствует любому одиночному символу, значение свойства Юникода Uppercase которого равно True, и \P{Uppercase} соответствует любому символу, значение свойства Uppercase которого равно False, и они могли бы быть записаны как \p{Uppercase=True} и \p{Uppercase=False} соответственно.

Эта формальность необходима, когда свойства не являются двоичными; то есть, если они могут принимать значения больше, чем просто True и False. Например, свойство Bidi_Class (см. "Типы символов для двунаправленной записи" ниже) может принимать несколько разных значений, таких как Left, Right, Whitespace и другие. Для сопоставления с ними необходимо указать как имя свойства (Bidi_Class), так и значение, с которым происходит сопоставление (Left, Right, и т. д.). Это делается, как в примерах выше, путём разделения двух компонентов знаком равенства (или, взаимозаменяемо, двоеточием), как \p{Bidi_Class: Left}.

Все свойства символов, определённые Юникодом, могут быть записаны в этих составных формах \p{property=value} или \p{property:value}, но Perl предоставляет некоторые дополнительные свойства, которые записаны только в единственной форме, а также сокращённые формы для всех двоичных свойств и некоторых других, описанных ниже, в которых вы можете опустить имя свойства и разделитель равенства или двоеточия.

У большинства свойств символов Юникода есть по крайней мере два синонима (или псевдонимы, если хотите): короткий, проще в наборе, и более длинный, более описательный, и поэтому более лёгкий для понимания. Таким образом, свойства "L" и "Letter" выше эквивалентны и могут быть использованы взаимозаменяемо. Точно так же, "Upper" — синоним "Uppercase", и мы могли бы написать \p{Uppercase} эквивалентно как \p{Upper}. Кроме того, обычно существуют различные синонимы для значений, которые может принимать свойство. Для двоичных свойств "True" имеет 3 синонима: "T", "Yes", и "Y"; и "False" аналогично имеет "F", "No", и "N". Но будьте осторожны. Короткий вариант значения одного свойства может не означать то же самое, что и тот же короткий вариант для другого. Таким образом, для свойства "General_Category" "L" означает "Letter", но для свойства Bidi_Class "L" означает "Left". Полный список свойств и синонимов находится в perluniprops.

Различия в регистре в именах и значениях свойств не имеют значения; таким образом, \p{Upper} означает то же самое, что и \p{upper} или даже \p{UpPeR} . Аналогично, вы можете добавлять или удалять нижние подчёркивания где угодно посреди слова, так что это также эквивалентно \p{U_p_p_e_r}. Также, пробелы не имеют значения, находясь рядом с небуквенными символами, такими как фигурные скобки и знаки равенства или двоеточия, так что \p{ Upper } и \p{ Upper_case : Y } эквивалентны им. В действительности, пробелы и даже дефисы обычно могут быть добавлены или удалены где угодно. Таким образом, даже \p{ Up-per case = Yes} эквивалентно. Всё это Юникод называет «свободным сопоставлением». В немногих случаях используется более строгое соответствие — в середине чисел и в свойствах расширения Perl, которые начинаются или заканчиваются нижним подчёркиванием. Более строгое сопоставление учитывает пробелы (кроме пробелов рядом с небуквенными символами), дефисы и нижние подчёркивания не в начале/конце слова.

Вы также можете использовать отрицание в \p{} и \P{} с помощью символа «^» (^) между первой фигурной скобкой и именем свойства: \p{^Tamil} равно \P{Tamil}.

Практически все свойства нечувствительны к регистру. Добавление модификатора регулярного выражения /i не меняет того, что они сопоставляют. Есть два набора, которые затронуты. Первый набор — Uppercase_Letter, Lowercase_Letter, и Titlecase_Letter, все из которых сопоставляются с Cased_Letter при нечувствительном к регистру сопоставлении. И второй набор — Uppercase, Lowercase, и Titlecase, все из которых сопоставляются с Cased при нечувствительном к регистру сопоставлении. Этот набор также включает подмножества PosixUpper и PosixLower , оба из которых при нечувствительном к регистру сопоставлении сопоставляются с PosixAlpha. (Разница между этими наборами заключается в том, что некоторые вещи, такие как римские цифры, существуют и в верхнем, и в нижнем регистре, поэтому они Cased, но не считаются буквами, поэтому не являются Cased_Letter.)

См. "За пределами кодовых точек Юникода" для особых случаев, когда сопоставляют свойства Юникода с несимволами Юникода.

Общая категория

Каждый символ Юникода назначен общей категории, которая является «самой распространённой категоризацией символа» (из http://www.unicode.org/reports/tr44).

Составной способ записи этих данных, например, \p{General_Category=Number} (короткий вариант: \p{gc:n}). Но Perl предоставляет сокращения, в которых всё до знака равенства или двоеточия опущено. Поэтому вы можете вместо этого просто написать \pN.

Вот короткие и длинные формы значений, которые может принимать свойство General Category:

Short       Long

L           Letter
LC, L&      Cased_Letter (that is: [\p{Ll}\p{Lu}\p{Lt}])
Lu          Uppercase_Letter
Ll          Lowercase_Letter
Lt          Titlecase_Letter
Lm          Modifier_Letter
Lo          Other_Letter

M           Mark
Mn          Nonspacing_Mark
Mc          Spacing_Mark
Me          Enclosing_Mark

N           Number
Nd          Decimal_Number (also Digit)
Nl          Letter_Number
No          Other_Number

P           Punctuation (also Punct)
Pc          Connector_Punctuation
Pd          Dash_Punctuation
Ps          Open_Punctuation
Pe          Close_Punctuation
Pi          Initial_Punctuation
            (may behave like Ps or Pe depending on usage)
Pf          Final_Punctuation
            (may behave like Ps or Pe depending on usage)
Po          Other_Punctuation

S           Symbol
Sm          Math_Symbol
Sc          Currency_Symbol
Sk          Modifier_Symbol
So          Other_Symbol

Z           Separator
Zs          Space_Separator
Zl          Line_Separator
Zp          Paragraph_Separator

C           Other
Cc          Control (also Cntrl)
Cf          Format
Cs          Surrogate
Co          Private_Use
Cn          Unassigned

Свойства, состоящие из одной буквы, соответствуют всем символам в любом подсвойстве из двух букв, начинающемся с той же буквы. LC и L& являются специальными: оба являются псевдонимами набора, состоящего из всего, что сопоставляется с Ll, Lu, и Lt.

Типы символов для двунаправленной записи

Поскольку разные письменности отличаются по направлению записи (например, иврит и арабский пишутся справа налево), Юникод предоставляет свойство Bidi_Class. Некоторые из значений, которые может принимать это свойство:

Value       Meaning

L           Left-to-Right
LRE         Left-to-Right Embedding
LRO         Left-to-Right Override
R           Right-to-Left
AL          Arabic Letter
RLE         Right-to-Left Embedding
RLO         Right-to-Left Override
PDF         Pop Directional Format
EN          European Number
ES          European Separator
ET          European Terminator
AN          Arabic Number
CS          Common Separator
NSM         Non-Spacing Mark
BN          Boundary Neutral
B           Paragraph Separator
S           Segment Separator
WS          Whitespace
ON          Other Neutrals

Это свойство всегда записывается в составной форме. Например, \p{Bidi_Class:R} соответствует символам, которые обычно пишутся справа налево. В отличие от свойства "General_Category", это свойство может иметь дополнительные значения, которые могут быть добавлены в будущих выпусках Юникода. Указанные выше значения составляли полный набор во многих выпусках Юникода, но другие были добавлены в версии 6.3; вы всегда можете найти текущие значения в perluniprops. И http://www.unicode.org/reports/tr9/ описывает, как их использовать.

Письменности

Языки мира написаны многими различными письменными системами. Это предложение (если вы не читаете его в переводе) написано латиницей, в то время как русский язык написан кириллицей, а греческий язык написан, ну, греческим алфавитом; японский язык в основном на иероглифах, или катакане и хирагане. Есть много других.

Свойства Юникода Script и Script_Extensions показывают, в какой письменности находится данный символ. Свойство Script_Extensions — улучшенная версия Script, как показано ниже. Любое из этих свойств может быть указано в составной форме, например \p{Script=Hebrew} (короткая форма: \p{sc=hebr}) или \p{Script_Extensions=Javanese} (короткая форма: \p{scx=java}). Кроме того, Perl предоставляет сокращения для всех имён свойств Script_Extensions. Вы можете опустить всё до знака равенства (или двоеточия) и просто написать \p{Latin} или \P{Cyrillic}. (Это не относится к Script, которое должно быть записано в составной форме. До Perl v5.26 единственная форма возвращала версию Script, но это было изменено, потому что Script_Extensions даёт лучшие результаты.)

Разница между этими двумя свойствами заключается в символах, используемых в нескольких письменных системах. Например, цифры от '0' до '9' используются во многих частях мира. Они помещены в письменность под названием Common. Другие символы используются только в нескольких письменных системах. Например, "KATAKANA-HIRAGANA DOUBLE HYPHEN" используется как в японских письменных системах, катакана и хирагана, но нигде больше. Свойство Script помещает все символы, используемые в нескольких письменных системах, в письменность Common, а свойство Script_Extensions помещает символы, используемые только в нескольких письменных системах, в каждую из этих письменных систем; при этом по-прежнему использует Common для символов, используемых во многих письменных системах. Таким образом, оба соответствуют:

"0" =~ /\p{sc=Common}/     # Matches
"0" =~ /\p{scx=Common}/    # Matches

и только первое из них соответствует:

"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{sc=Common}  # Matches
"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{scx=Common} # No match

И только последние два из них соответствуют:

"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{sc=Hiragana}  # No match
"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{sc=Katakana}  # No match
"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{scx=Hiragana} # Matches
"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}" =~ /\p{scx=Katakana} # Matches

Script_Extensions — это усовершенствованный Script, в котором в скрипте Common меньше символов, а в других скриптах — соответственно, больше. Он новый в версии Unicode 6.0, и его данные, вероятно, значительно изменятся в последующих выпусках, по мере решения вопросов. Новый код, вероятно, должен использовать Script_Extensions, а не простой Script. Если вы скомпилируете Perl с версией Unicode, в которой нет Script_Extensions, расширения Perl для одиночной формы будут вместо этого ссылаться на простое свойство Script. Если вы скомпилируете с версией Unicode, в которой нет свойства Script, эти расширения вообще не будут определены.

(На самом деле, помимо Common, в скрипте Inherited содержатся символы, используемые в нескольких скриптах. Это модификаторы, которые наследуют значение скрипта управляющего символа. Некоторые из них используются во многих скриптах и, следовательно, попадают в Inherited как в Script, так и в Script_Extensions. Другие используются только в нескольких скриптах, поэтому находятся в Inherited в Script, но не в Script_Extensions.)

Стоит подчеркнуть, что в Unicode есть несколько различных наборов цифр, эквивалентных 0-9, которые можно сопоставить с помощью \d в регулярном выражении. Если они используются только в одном языке, они находятся в Script и Script_Extensions этого языка. Если они используются в нескольких скриптах, они будут в sc=Common, но только если они используются во многих скриптах, они должны находиться в scx=Common.

В приведенном выше объяснении пропущены некоторые детали; обратитесь к UAX#24 «Свойство скрипта Unicode»: http://www.unicode.org/reports/tr24.

Полный список скриптов и их сокращений находится в perluniprops.

Использование префикса "Is"

Для обеспечения обратной совместимости (с древними Perl 5.6) все свойства, которые можно записать без использования составной формы, упомянутой до сих пор, могут иметь префикс Is или Is_, добавленный к их имени, например, \P{Is_Lu} равно \P{Lu}, а \p{IsScript:Arabic} равно \p{Arabic}.

Блоки

В дополнение к скриптам Unicode также определяет блоки символов. Разница между скриптами и блоками заключается в том, что понятие скриптов ближе к естественным языкам, а понятие блоков больше относится к искусственной группировке, основанной на группах символов Unicode с последовательными порядковыми значениями. Например, блок "Basic Latin" включает все символы, порядковые значения которых находятся в диапазоне от 0 до 127 включительно; другими словами, символы ASCII. Скрипт "Latin" содержит некоторые буквы из этого, а также несколько других блоков, таких как "Latin-1 Supplement", "Latin Extended-A", и т.д., но он не содержит все символы из этих блоков. Например, он не содержит цифр от 0 до 9, так как эти цифры общие для многих скриптов и, следовательно, находятся в скрипте Common.

Для получения дополнительной информации о скриптах и блоках см. UAX#24 «Свойство скрипта Unicode»: http://www.unicode.org/reports/tr24

Свойства Script_Extensions или Script, скорее всего, являются теми, которые вам нужно использовать при обработке естественного языка; свойство Block может быть полезно в работе с основами Unicode.

Имена блоков сопоставляются в составной форме, например \p{Block: Arrows} или \p{Blk=Hebrew}.

В отличие от большинства других свойств, только несколько имён блоков имеют определенное сокращённое имя в Unicode.

Perl также определяет синонимы для свойства блока в одиночной форме в тех случаях, когда это не конфликтует с чем-то другим. Но не используйте их, потому что они нестабильны. Поскольку это расширения Perl, они подчиняются официальным именам свойств Unicode; Unicode не знает и не заботится о расширениях Perl. Может случиться, что имя, которое сейчас означает расширение Perl, в будущем будет изменено без предупреждения, чтобы обозначать другое свойство Unicode в будущей версии интерпретатора Perl, использующего более позднюю версию Unicode, и ваш код перестанет работать. Эти расширения упоминаются здесь для полноты: возьмите имя блока и добавьте один из префиксов: In (например, \p{Blk=Arrows} в настоящее время можно записать как \p{In_Arrows}); или иногда Is (например, \p{Is_Arrows}); или иногда вообще без префикса (\p{Arrows}). На момент написания (Unicode 9.0) нет конфликтов при использовании префикса In_, но есть много конфликтов с другими двумя формами. Например, \p{Is_Hebrew} и \p{Hebrew} означают \p{Script_Extensions=Hebrew}, что НЕ то же самое, что \p{Blk=Hebrew}.

Наш совет раньше был использовать префикс In_ как способ в одиночной форме указать блок. Но Unicode 8.0 добавил свойства, имена которых начинаются с In, и теперь ясно, что это просто удача, что до сих пор не было конфликтов. Использование In лишь немного меньше, чем Blk:, а значение последнего более ясно и гарантированно никогда не будет конфликтовать. Так что не рискуйте. Используйте \p{Blk=foo} для нового кода. И убедитесь, что блок — это то, что вы действительно хотите сделать. В большинстве случаев вместо блоков нужно использовать скрипты.

Полный список блоков находится в perluniprops.

Другие свойства

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

Unicode определяет все свои свойства в составной форме, поэтому все свойства в одиночной форме являются расширениями Perl. Большинство из них — это просто синонимы для свойств Unicode, но некоторые — настоящие расширения, включая несколько, которые находятся в составной форме. И немало из них на самом деле рекомендуются Unicode (в http://www.unicode.org/reports/tr18).

Этот раздел содержит некоторые детали обо всех расширениях, которые не являются просто синонимами свойств Unicode в составной форме (для этих свойств вам нужно обратиться к стандарту Unicode.

END_OF_DOCUMENT_MARKER
\p{All}

Это соответствует всем возможным кодовым точкам. Это эквивалентно qr/./s. В отличие от всех других совпадений свойств, определенных пользователем \p{}, если это свойство сопоставляется с не-Unicode кодовой точкой, предупреждение никогда не генерируется (см. "За пределами кодовых точек Unicode" ниже).

\p{Alnum}

Это соответствует любой \p{Alphabetic} или \p{Decimal_Number} символу.

\p{Any}

Это соответствует любой из 1 114 112 кодовых точек Unicode. Это синоним для \p{Unicode}.

\p{ASCII}

Это соответствует любому из 128 символов в наборе символов US-ASCII, который является подмножеством Unicode.

\p{Assigned}

Это соответствует любой назначенной кодовой точке; то есть любой кодовой точке, чья общая категория не является Unassigned (или, эквивалентно, не Cn).

\p{Blank}

Это то же самое, что и \h и \p{HorizSpace}: символ, изменяющий горизонтальное расстояние.

\p{Decomposition_Type: Non_Canonical} (Short: \p{Dt=NonCanon})

Соответствует символу, имеющему неканоническое разложение.

В разделе "Расширенные графические кластеры (логические символы)" выше говорилось о канонических разложениях. Однако многие другие символы имеют другой тип разложения, «совместимое» или «неканоническое» разложение. Последовательности, образующие эти разложения, не считаются канонически эквивалентными прекомпонованному символу. Пример — "SUPERSCRIPT ONE". Он немного похож на обычную цифру 1, но не совсем; его разложение на цифру 1 называется «совместимым» разложением, конкретно — «супер» разложением. Существует несколько таких совместимых разложений (см. http://www.unicode.org/reports/tr44), включая одно, называемое «compat», которое означает какой-то смешанный тип разложения, не вписывающийся в другие категории разложений, выбранные Unicode.

Обратите внимание, что у большинства символов Unicode нет разложения, поэтому их тип разложения — "None".

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

\p{Graph}

Соответствует любому графическому символу. Теоретически это означает символ, который на принтере заставит чернила быть использованными.

\p{HorizSpace}

Это то же самое, что и \h и \p{Blank}: символ, изменяющий горизонтальное расстояние.

\p{In=*}

Это синоним для \p{Present_In=*}

\p{PerlSpace}

Это то же самое, что и \s, ограниченное ASCII, а именно [ \f\n\r\t] и, начиная с Perl v5.18, вертикальная табуляция.

Мнемоника: пробел Perl (оригинальный)

\p{PerlWord}

Это то же самое, что и \w, ограниченное ASCII, а именно [A-Za-z0-9_]

Мнемоника: слово Perl (оригинальное).

\p{Posix...}

Существует несколько таких вариантов, которые являются эквивалентами, использующими \p{} обозначение, для классов Posix и описаны в "Классы символов Posix" в perlrecharclass.

\p{Present_In: *} (Short: \p{In=*})

Это свойство используется, когда необходимо знать, в каких версиях Unicode находится символ.

«*» выше обозначает некоторый двухзначный номер версии Unicode, например 1.1 или 4.0; или «*» также может быть Unassigned. Это свойство будет соответствовать кодовым точкам, окончательное назначение которых было утверждено на момент выпуска Unicode, заданной номером версии; \p{Present_In: Unassigned} будут соответствовать тем кодовым точкам, значение которых ещё не назначено.

Например, U+0041 "LATIN CAPITAL LETTER A" был присутствовал в самом первом доступном выпуске Unicode, который является 1.1, поэтому это свойство верно для всех допустимых версий «*». С другой стороны, U+1EFF был назначен только в версии 5.1, когда он стал "LATIN SMALL LETTER Y WITH LOOP", поэтому единственными «*», которые соответствуют ему, являются 5.1, 5.2 и более поздние.

Unicode предоставляет свойство Age, от которого это происходит. Проблема с Age в том, что строгая интерпретация (которую принимает Perl) делает его совпадающим с точной версией выпуска, в которой вводится значение кодовой точки. Таким образом, U+0041 будет соответствовать только 1.1; а U+1EFF только 5.1. Это обычно не то, что нужно.

В некоторых реализациях свойства Age, отличных от Perl, его значение может быть изменено на то же самое, что и свойство Perl Present_In; просто имейте это в виду.

Ещё одной проблемой с обоими этими свойствами является то, что определение состоит не в том, что кодовая точка назначена, а в том, что значение кодовой точки определено. Это связано с тем, что 66 кодовых точек всегда будут неназначенными, и поэтому Age для них — версия Unicode, в которой было принято решение сделать их таковыми. Например, U+FDD0 должен быть постоянно неназначен как символ, и решение об этом было принято в версии 3.1, поэтому \p{Age=3.1} соответствует этому символу, как и \p{Present_In: 3.1} и выше.

\p{Print}

Это соответствует любому графическому или пробельному символу, за исключением управляющих символов.

\p{SpacePerl}

Это то же самое, что и \s, включая символы, находящиеся за пределами ASCII.

Мнемоника: Пробел, как он изменён Perl. (Он не включает вертикальную табуляцию до v5.18, которую и стандарт Posix, и Unicode считают пробелами.)

\p{Title} и \p{Titlecase}

При чувствительном к регистру сопоставлении оба соответствуют тем же кодовым точкам, что и \p{General Category=Titlecase_Letter} (\p{gc=lt}). Разница в том, что при сопоставлении без учёта регистра /i, они соответствуют тому же, что и \p{Cased}, тогда как \p{gc=lt} соответствует \p{Cased_Letter.

\p{Unicode}

Это соответствует любой из 1 114 112 кодовых точек Unicode. \p{Any}.

\p{VertSpace}

Это то же самое, что и \v: символ, изменяющий вертикальное расстояние.

\p{Word}

Это то же самое, что и \w, включая более 100 000 символов за пределами ASCII.

\p{XPosix...}

Есть несколько таких вариантов, представляющих собой стандартные классы Posix, расширенные до всего диапазона Unicode. Они описаны в "Классы символов Posix" в perlrecharclass.

Свойства символов, определённые пользователем

Вы можете определить свои собственные двоичные свойства символов, определив подпрограммы, имена которых начинаются с "In" или "Is". (Экспериментальная функция "(?[ ])" в perlre предоставляет альтернативу, которая позволяет более сложные определения.) Подпрограммы могут быть определены в любом пакете. Определённые пользователем свойства могут использоваться в конструкциях регулярных выражений \p{} и \P{}; если вы используете свойство, определённое пользователем из пакета, отличного от вашего, вы должны указать его пакет в конструкции \p{} или \P{}.

# assuming property Is_Foreign defined in Lang::
package main;  # property package name required
if ($txt =~ /\p{Lang::IsForeign}+/) { ... }

package Lang;  # property package name not required
if ($txt =~ /\p{IsForeign}+/) { ... }

Обратите внимание, что эффект возникает во время компиляции и является неизменяемым после определения. Однако подпрограммы получают один параметр, который равен 0, если в формате сопоставления используется регистрозависимое сопоставление, и не 0, если используется регистронезависимое сопоставление. Подпрограмма может возвращать различные значения в зависимости от значения флага, и один набор значений будет неизменно действовать для всех регистрозависимых совпадений, а другой — для всех регистронезависимых совпадений.

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

Подпрограммы должны возвращать строку в специальном формате, содержащую одну или несколько строк, разделённых переводом строки. Каждая строка должна быть одной из следующих:

  • Одно шестнадцатеричное число, обозначающее кодовую точку, которую нужно включить.

  • Два шестнадцатеричных числа, разделённых горизонтальным пробелом (пробел или табуляция), обозначающие диапазон кодовых точек, которые нужно включить.

  • Что-то для включения, с префиксом "+": встроенное свойство символа (с префиксом "utf8::") или полностью квалифицированное (включая имя пакета) свойство символа, определённое пользователем, чтобы представить все символы в этом свойстве; две шестнадцатеричные кодовые точки для диапазона; или одна шестнадцатеричная кодовая точка.

  • Что-то для исключения, с префиксом "-": существующее свойство символа (с префиксом "utf8::") или полностью квалифицированное (включая имя пакета) свойство символа, определённое пользователем, чтобы представить все символы в этом свойстве; две шестнадцатеричные кодовые точки для диапазона; или одна шестнадцатеричная кодовая точка.

  • Что-то для отрицания, с префиксом "!": существующее свойство символа (с префиксом "utf8::") или полностью квалифицированное (включая имя пакета) свойство символа, определённое пользователем, чтобы представить все символы в этом свойстве; две шестнадцатеричные кодовые точки для диапазона; или одна шестнадцатеричная кодовая точка.

  • Что-то для пересечения, с префиксом "&": существующее свойство символа (с префиксом "utf8::") или полностью квалифицированное (включая имя пакета) свойство символа, определённое пользователем, чтобы представить все символы, кроме символов в этом свойстве; две шестнадцатеричные кодовые точки для диапазона; или одна шестнадцатеричная кодовая точка.

Например, чтобы определить свойство, охватывающее оба японских слоговых алфавита (хирагана и катакана), вы можете определить

sub InKana {
    return <<END;
3040\t309F
30A0\t30FF
END
}

Представьте, что маркер конца здесь находится в начале строки. Теперь вы можете использовать \p{InKana} и \P{InKana}.

Вы также могли бы использовать существующие имена свойств блоков:

sub InKana {
    return <<'END';
+utf8::InHiragana
+utf8::InKatakana
END
}

Предположим, вы хотели соответствовать только выделенным символам, а не исходным диапазонам блоков: другими словами, вы хотите удалить невыделенные символы:

sub InKana {
    return <<'END';
+utf8::InHiragana
+utf8::InKatakana
-utf8::IsCn
END
}

Отрицание полезно для определения (на удивление!) отрицаемых классов.

sub InNotKana {
    return <<'END';
!utf8::InHiragana
-utf8::InKatakana
+utf8::IsCn
END
}

Это будет соответствовать всем не-Unicode кодовым точкам, поскольку каждая из них не входит в Кана. При желании можно использовать пересечение, чтобы исключить их, как показано в этом изменённом примере:

sub InNotKana {
    return <<'END';
!utf8::InHiragana
-utf8::InKatakana
+utf8::IsCn
&utf8::Any
END
}

&utf8::Any должна быть последней строкой в определении.

Пересечение обычно используется для получения общих символов, соответствующих двум (или более) классам. Важно помнить, что не следует использовать "&" для первого набора; это означало бы пересечение с пустым множеством, что приводит к пустому множеству.

В отличие от совпадений свойств \p{} , не определённых пользователем, никакие предупреждения никогда не генерируются, если эти свойства сопоставляются с кодовой точкой, не являющейся Unicode (см. "За пределами кодовых точек Unicode" ниже).

Пользовательские сопоставления регистров (только для продвинутых пользователей)

Эта функция была удалена начиная с Perl 5.16. Модуль CPAN Unicode::Casing предоставляет более функциональные возможности без недостатков, которые были у этой функции. Если вы используете Perl, более раннюю, чем 5.16, то эта функция была наиболее подробно описана в версии 5.14 этого документа: http://perldoc.perl.org/5.14.0/perlunicode.html#User-Defined-Case-Mappings-%28for-serious-hackers-only%29

Кодировки символов для ввода и вывода

См. Encode.

Уровень поддержки Unicode для регулярных выражений

Следующий список поддерживаемых функций Unicode для регулярных выражений описывает все функции, которые в настоящее время напрямую поддерживаются ядром Perl. Ссылки на «Уровень N» и номера разделов относятся к UTS#18 "Unicode Regular Expressions", версия 13, ноябрь 2013 года.

Уровень 1 - Базовая поддержка Unicode

RL1.1   Hex Notation                     - Done          [1]
RL1.2   Properties                       - Done          [2]
RL1.2a  Compatibility Properties         - Done          [3]
RL1.3   Subtraction and Intersection     - Experimental  [4]
RL1.4   Simple Word Boundaries           - Done          [5]
RL1.5   Simple Loose Matches             - Done          [6]
RL1.6   Line Boundaries                  - Partial       [7]
RL1.7   Supplementary Code Points        - Done          [8]
[1] \N{U+...} и \x{...}
[2] \p{...} \P{...}. Это требование относится к минимальному списку свойств. Perl поддерживает эти и все остальные свойства символов Unicode, как требуется R2.7 (см. "Свойства символов Unicode" выше).
[3] Perl имеет \d \D \s \S \w \W \X [:prop:] [:^prop:], а также все свойства, указанные в http://www.unicode.org/reports/tr18/#Compatibility_Properties. Они описаны выше в разделе "Другие свойства"
[4]

Экспериментальная функция "(?[...])", начиная с v5.18, решает эту задачу.

См. "(?[ ])" в perlre. Если вы не хотите использовать экспериментальную функцию, можно использовать одну из следующих:

  • Проверка соответствия в регулярных выражениях

    Можно имитировать вычитание классов с помощью проверки соответствия. Например, то, что UTS#18 может записать как

    [{Block=Greek}-[{UNASSIGNED}]]

    в Perl можно записать как:

    (?!\p{Unassigned})\p{Block=Greek}
    (?=\p{Assigned})\p{Block=Greek}

    Но в этом конкретном примере, вы, вероятно, хотите

    \p{Greek}

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

  • Модуль CPAN Unicode::Regex::Set

    Он реализует полную синтаксис группирования, пересечения, объединения и удаления (вычитания) UTS#18.

  • "Пользовательские свойства символов"

    "+" для объединения, "-" для удаления (разности множеств), "&" для пересечения

[5] \b \B соответствуют большинству, но не всем, деталям этого требования, но \b{wb} и \B{wb} соответствуют, а также более строгому R2.3.
[6]

Обратите внимание, что Perl выполняет полное свёртывание регистров при сопоставлении, а не простое:

Например, U+1F88 эквивалентно U+1F00 U+03B9, а не только U+1F80. Это различие имеет значение в основном для определённых греческих прописных букв с определёнными модификаторами: полное свёртывание регистров декомпозирует букву, в то время как простое свёртывание регистров сопоставило бы её с одним символом.

[7]

Причина, по которой это считается частично реализованным, заключается в том, что Perl имеет qr/\b{lb}/ и Unicode::LineBreak , которые соответствуют UAX#14 "Алгоритм разбиения строк Unicode". Конструктор регулярных выражений обеспечивает стандартное поведение, в то время как более сложный модуль обеспечивает настраиваемое разбиение строк.

Но Perl рассматривает \n как разделитель начала и конца строки, в то время как Unicode указывает на больше символов, которые должны интерпретироваться таким образом.

Это:

VT   U+000B  (\v in C)
FF   U+000C  (\f)
CR   U+000D  (\r)
NEL  U+0085
LS   U+2028
PS   U+2029

^ и $ в шаблонах регулярных выражений должны соответствовать всем этим, но не соответствуют. Эти символы также не, но должны, влиять на <> $., и номера строк скрипта.

Кроме того, строки не должны разбиваться внутри CRLF (то есть между \r и \n нет пустой строки). Для CRLF, попробуйте слой :crlf (см. PerlIO).

[8] UTF-8/UTF-EBDDIC, используемый в Perl, позволяет не только U+10000 до U+10FFFF , но и за пределами U+10FFFF

Уровень 2 - Расширенная поддержка Unicode

RL2.1   Canonical Equivalents           - Retracted     [9]
                                          by Unicode
RL2.2   Extended Grapheme Clusters      - Partial       [10]
RL2.3   Default Word Boundaries         - Done          [11]
RL2.4   Default Case Conversion         - Done
RL2.5   Name Properties                 - Done
RL2.6   Wildcard Properties             - Missing
RL2.7   Full Properties                 - Done
[9] Unicode переписал эту часть UTS#18, чтобы сказать, что получение канонического эквивалента (см. UAX#15 "Формы нормализации Unicode") в основном должно выполняться на уровне программиста. Используйте NFD для записи как ваших регулярных выражений, так и текста для их сопоставления (вы можете использовать Unicode::Normalize).
[10] Perl имеет \X и \b{gcb} , но у нас нет режима "Кластер графем".
[11] см. UAX#29 "Сегментация текста Unicode",

Уровень 3 - Настраиваемая поддержка

RL3.1   Tailored Punctuation            - Missing
RL3.2   Tailored Grapheme Clusters      - Missing       [12]
RL3.3   Tailored Word Boundaries        - Missing
RL3.4   Tailored Loose Matches          - Retracted by Unicode
RL3.5   Tailored Ranges                 - Retracted by Unicode
RL3.6   Context Matching                - Missing       [13]
RL3.7   Incremental Matches             - Missing
RL3.8   Unicode Set Sharing             - Unicode is proposing
                                          to retract this
RL3.9   Possible Match Sets             - Missing
RL3.10  Folded Matching                 - Retracted by Unicode
RL3.11  Submatchers                     - Missing
[12] Perl имеет Unicode::Collate, но он не интегрирован с регулярными выражениями. См. UTS#10 "Алгоритмы сортировки Unicode".
[13] Perl имеет (?<=x) и (?=x), но проверки соответствия вперёд или назад должны видеть за пределами целевого подстроки

Кодировки Unicode

Символам Unicode присваиваются кодовые точки, которые являются абстрактными числами. Для использования этих чисел требуются различные кодировки.

  • UTF-8

    UTF-8 — это кодировка переменной длины (от 1 до 4 байт), независимая от порядка байтов. В большинстве документации Perl, включая и эту часть, термин «UTF-8» означает также «UTF-EBCDIC». Однако в этом разделе «UTF-8» относится только к кодировке, используемой на платформах ASCII. Это расширение 7-битного US-ASCII, поэтому любая кодировка в ASCII имеет идентичное представление при кодировании в UTF-8.

    Следующая таблица взята из Unicode 3.2.

    Code Points            1st Byte  2nd Byte  3rd Byte 4th Byte
    
      U+0000..U+007F       00..7F
      U+0080..U+07FF     * C2..DF    80..BF
      U+0800..U+0FFF       E0      * A0..BF    80..BF
      U+1000..U+CFFF       E1..EC    80..BF    80..BF
      U+D000..U+D7FF       ED        80..9F    80..BF
      U+D800..U+DFFF       +++++ utf16 surrogates, not legal utf8 +++++
      U+E000..U+FFFF       EE..EF    80..BF    80..BF
     U+10000..U+3FFFF      F0      * 90..BF    80..BF    80..BF
     U+40000..U+FFFFF      F1..F3    80..BF    80..BF    80..BF
    U+100000..U+10FFFF     F4        80..8F    80..BF    80..BF

    Обратите внимание на пробелы, обозначенные «*» перед несколькими байтовыми записями выше. Они вызваны тем, что в легальном UTF-8 избегаются некратчайшие кодировки: теоретически возможно закодировать отдельную кодовую точку в UTF-8 различными способами, но это явно запрещено, и всегда должна использоваться кратчайшая возможная кодировка (и именно это делает Perl).

    Другой способ посмотреть на это — через биты:

                   Code Points  1st Byte  2nd Byte  3rd Byte  4th Byte
    
                      0aaaaaaa  0aaaaaaa
              00000bbbbbaaaaaa  110bbbbb  10aaaaaa
              ccccbbbbbbaaaaaa  1110cccc  10bbbbbb  10aaaaaa
    00000dddccccccbbbbbbaaaaaa  11110ddd  10cccccc  10bbbbbb  10aaaaaa

    Как вы можете видеть, продолжение байтов начинается с "10", а ведущие биты начального байта указывают, сколько байтов находится в кодированном символе.

    Исходная спецификация UTF-8 допускала до 6 байтов, чтобы разрешить кодирование чисел до 0x7FFF_FFFF. Perl продолжает допускать их и расширил это до 13 байтов для кодирования кодовых точек до тех, что могут поместиться в 64-битовое слово. Однако Perl выведет предупреждение, если вы выведите какое-либо из этих значений как непереносимое; а в соответствии со строгими протоколами ввода UTF-8 они запрещены. Кроме того, использование кодовой точки, большей, чем может вместить целочисленная переменная со знаком на вашей системе, устарело. На 32-битных системах ASCII это означает 0x7FFF_FFFF является допустимым максимальным значением (намного выше на 64-битных системах).

  • UTF-EBCDIC

    Подобно UTF-8, но безопасный для EBCDIC, так же как UTF-8 безопасен для ASCII. Это означает, что все основные символы (включая все те, которые имеют эквиваленты ASCII (например, "A", "0", "%", и т. д.) одинаковы как в EBCDIC, так и в UTF-EBCDIC).

    UTF-EBCDIC используется на платформах EBCDIC. В целом, для представления данной кодовой точки требуется больше байтов, чем в UTF-8; самые большие кодовые точки Unicode занимают 5 байтов (вместо 4 в UTF-8), и, расширенные для 64-битовых слов, они используют 14 байтов вместо 13 байтов в UTF-8.

  • UTF-16, UTF-16BE, UTF-16LE, суррогаты и BOM (знаки порядка байтов)

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

    Как и UTF-8, UTF-16 — это кодировка переменной длины, но где UTF-8 использует 8-битные кодовые единицы, UTF-16 использует 16-битные кодовые единицы. Все кодовые точки занимают либо 2, либо 4 байта в UTF-16: кодовые точки U+0000..U+FFFF хранятся в одной 16-битной единице, а кодовые точки U+10000..U+10FFFF — в двух 16-битных единицах. В последнем случае используются суррогаты, первая 16-битная единица является высоким суррогатом, а вторая — низким суррогатом.

    Суррогаты — это кодовые точки, отведенные для кодирования U+10000..U+10FFFF диапазона кодовых точек Unicode в парах 16-битных единиц. Высокие суррогаты находятся в диапазоне U+D800..U+DBFF, а низкие суррогаты — в диапазоне U+DC00..U+DFFF. Кодирование суррогатов

    $hi = ($uni - 0x10000) / 0x400 + 0xD800;
    $lo = ($uni - 0x10000) % 0x400 + 0xDC00;

    и декодирование

    $uni = 0x10000 + ($hi - 0xD800) * 0x400 + ($lo - 0xDC00);

    Из-за 16-битной структуры UTF-16 зависит от порядка байтов. Сам UTF-16 может использоваться для вычислений в памяти, но если требуется хранение или передача, необходимо выбрать кодировки UTF-16BE (big-endian) или UTF-16LE (little-endian).

    Это порождает еще одну проблему: что если вам известно, что ваши данные в UTF-16, но вы не знаете, какой порядок байтов? Знаки порядка байтов, или BOM , являются решением этой проблемы. В Unicode зарезервирован специальный символ, выполняющий функцию знака порядка байтов: символ с кодовой точкой U+FEFF является BOM.

    Хитрость заключается в том, что если вы прочтете BOM, вы узнаете порядок байтов, поскольку, если он был записан на платформе big-endian, вы прочтете байты 0xFE 0xFF, а если он был записан на платформе little-endian, вы прочтете байты 0xFF 0xFE. (И если исходная платформа записывала данные в ASCII платформы UTF-8, вы прочтете байты 0xEF 0xBB 0xBF.)

    Способ работы этой хитрости заключается в том, что символ с кодовой точкой U+FFFE не должен находиться в потоках ввода, поэтому последовательность байтов 0xFF 0xFE однозначно «BOM, представленная в формате little-endian», и не может быть U+FFFE, представленной в формате big-endian».

    Суррогаты не имеют смысла в Unicode за пределами их использования в парах для представления других кодовых точек. Однако Perl позволяет им представляться по отдельности внутри, например, сказав chr(0xD801), чтобы все кодовые точки, а не только те, которые подходят для открытого обмена, могли быть представлены. Unicode определяет семантику для них, например, их "General_Category" — это "Cs". Но поскольку их использование несколько опасно, Perl выведет предупреждение (используя категорию предупреждений "surrogate", которая является подкатегорией "utf8" ), если будет предпринята попытка выполнить действия, такие как привести к нижнему регистру одно, или выполнить сопоставление без учета регистра, или вывести их. (Но не пытайтесь это на Perl-версиях до 5.14).

  • UTF-32, UTF-32BE, UTF-32LE

    Семейство UTF-32 в значительной степени аналогично семейству UTF-16, за исключением того, что единицы составляют 32 бита, и поэтому схема суррогатов не требуется. UTF-32 — это кодировка с фиксированной длиной. BOM подписи составляют 0x00 0x00 0xFE 0xFF для BE и 0xFF 0xFE 0x00 0x00 для LE.

  • UCS-2, UCS-4

    Устаревшие кодировки с фиксированной длиной, определённые стандартом ISO 10646. UCS-2 — это 16-битная кодировка. В отличие от UTF-16, UCS-2 не расширяется за пределы U+FFFF, потому что не использует суррогатов. UCS-4 — это 32-битная кодировка, функционально идентичная UTF-32 (различие состоит в том, что UCS-4 не запрещает ни суррогаты, ни кодовые точки, большие, чем 0x10_FFFF).

  • UTF-7

    Безопасная 7-битная кодировка (не 8-битная), которая полезна, если транспорт или хранение не являются 8-битными. Определена RFC 2152.

Непечатные кодовые точки

В Unicode зарезервированы 66 кодовых точек как «непечатные кодовые точки». У них все Unassigned (Cn) "General_Category", и ни одному из них никогда не будет назначен символ. Это 32 кодовых точки между U+FDD0 и U+FDEF включительно, а также 34 кодовых точки:

U+FFFE   U+FFFF
U+1FFFE  U+1FFFF
U+2FFFE  U+2FFFF
...
U+EFFFE  U+EFFFF
U+FFFFE  U+FFFFF
U+10FFFE U+10FFFF

До версии Unicode 7.0 непечатные символы были «запрещены для использования в открытом обмене данными Unicode», так что код, обрабатывающий эти потоки, мог использовать эти кодовые точки как метки, которые можно было смешивать с данными символов, и они всегда отличались бы от этих данных. (Курсив выше и в следующем абзаце добавлены в этом документе.)

Unicode 7.0 изменил формулировку, что они «не рекомендуются для использования в открытом обмене данными Unicode». В стандарте 7.0 говорится:

    «Если в открытом обмене получен непечатный символ, приложение не обязано его интерпретировать. Однако рекомендуется распознать его как непечатный символ и предпринять соответствующие действия, такие как замена его на U+FFFD символ замены, чтобы указать на проблему в тексте. Не рекомендуется просто удалять непечатные кодовые точки из такого текста, из-за потенциальных проблем безопасности, вызванных удалением неинтерпретированных символов. (См. пункт согласования C7 в разделе 3.2, Требования к согласованию, и Технический отчёт Unicode #36, «Соображения безопасности Unicode»).»

Это изменение было сделано, потому что было обнаружено, что различные коммерческие инструменты, такие как редакторы или средства контроля версий, были написаны таким образом, что они не обрабатывали файлы программ, которые использовали эти кодовые точки, фактически почти полностью исключая их использование! И это никогда не было намерением. Они всегда должны были быть применимы внутри приложения или группы взаимодействующих приложений по желанию.

Если вы пишете код, например, редактор, который должен быть способен обрабатывать любые данные Unicode, то вы не должны сами использовать эти кодовые точки, а вместо этого позволить им входить в данные на входе. Если вам нужны метки, они должны быть чем-то, что не является допустимым Unicode. Для данных UTF-8 вы можете использовать байты 0xC1 и 0xC2 в качестве меток, поскольку они никогда не встречаются в правильно сформированных данных UTF-8. (Существуют эквиваленты для UTF-EBCDIC). Вы также можете хранить свои кодовые точки Unicode в целочисленных переменных и использовать отрицательные значения в качестве меток.

Если вы не пишете такой инструмент, то принимать ли непечатные символы на вход — решать вам (хотя стандарт рекомендует этого не делать). Если вы выполните строгую проверку входного потока с помощью Perl, эти кодовые точки по-прежнему будут запрещены. Это для обеспечения обратной совместимости (в противном случае могут открыться потенциальные бреши в безопасности, так как неосторожное приложение, написанное с предположением, что непечатные символы будут отфильтрованы до его достижения, теперь без предупреждения может начать их получать). Чтобы выполнить строгую проверку, вы можете использовать слой :encoding('UTF-8').

Perl по-прежнему выводит предупреждение (используя категорию предупреждений "nonchar", которая является подкатегорией "utf8" ), если предпринимается попытка вывести непечатные символы.

За пределами кодовых точек Unicode

Максимальная кодовая точка Unicode — U+10FFFF, и Unicode определяет операции только над кодовыми точками до этого значения. Но Perl работает с кодовыми точками до максимального допустимого беззнакового числа, доступного на платформе. Однако Perl не будет принимать их из входных потоков, если не используются мягкие правила, и выведет предупреждение (используя категорию предупреждений "non_unicode", которая является подкатегорией "utf8" ), если какие-либо из них будут выведены.

Поскольку правила Unicode не определены для этих кодовых точек, если выполняется определённая операция Unicode, Perl использует, по нашему мнению, разумные правила, при этом обычно выводит предупреждение, используя категорию "non_unicode" . Например, uc("\x{11_0000}") сгенерирует такое предупреждение, вернув входной параметр в качестве результата, так как Perl определяет верхний регистр каждой кодовой точки, не входящей в Unicode, как саму кодовую точку. (Все операции изменения регистра, а не только преобразования в верхний регистр, работают таким образом.)

Ситуация с сопоставлением свойств Unicode в регулярных выражениях, конструкциях \p{} и \P{} с этими кодовыми точками не столь однозначна, и способ их обработки менялся по мере накопления опыта.

Одним из вариантов является обработка любого сопоставления с этими кодовыми точками как неопределённого. Но поскольку Perl не имеет понятия о неопределённом сопоставлении, он преобразует это в провал или FALSE. Это почти, но не совсем, то, что Perl делал с версии 5.14 (когда использование этих кодовых точек стало в целом надёжным) до версии 5.18. Разница заключается в том, что Perl обрабатывал все \p{} сопоставления как провалы, но все \P{} сопоставления как успешные.

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

chr(0x110000) =~ \p{ASCII_Hex_Digit=True}      # Failed on <= v5.18
chr(0x110000) =~ \p{ASCII_Hex_Digit=False}     # Failed! on <= v5.18

То есть, он обрабатывал оба совпадения как неопределённые и преобразовывал это в ложь (выводя предупреждение в каждом случае). Первый случай — ожидаемый результат, но второй, вероятно, неинтуитивен: «Как оба могут быть ложными, когда они являются дополнениями?» Другой проблемой было то, что реализация оптимизировала многие совпадения свойств Юникода до уже существующих более простых и быстрых операций, которые не вызывают предупреждение. Мы решили не отказываться от этих оптимизаций, которые помогают подавляющему большинству совпадений, только для того, чтобы сгенерировать предупреждение для маловероятного случая, когда символ с кодом выше Юникода сопоставляется с каким-либо свойством.

В результате этих проблем, начиная с версии 5.20, Perl обрабатывает символы без Юникода как типичные неназначенные символы Юникода и соответствует им соответственно. (Примечание: Юникод имеет нетипичные неназначенные символы. Например, он имеет символы без символа и такие, которые, когда они присваиваются, предназначены для написания справа налево, как арабский и иврит. Perl предполагает, что ни один символ без Юникода не имеет никаких атипичных свойств.)

Perl в большинстве случаев будет выводить предупреждение при сопоставлении символа с кодом выше Юникода с свойством Юникода, когда результат является TRUE для \p{}, и FALSE для \P{}. Например:

chr(0x110000) =~ \p{ASCII_Hex_Digit=True}      # Fails, no warning
chr(0x110000) =~ \p{ASCII_Hex_Digit=False}     # Succeeds, with warning

В обоих этих примерах символ, который сопоставляется, не имеет Юникода, поэтому Юникод не определяет, как он должен сопоставляться. Это явно не десятичная цифра в ASCII, поэтому в первом примере должно произойти явное неудачное сопоставление, и так оно и происходит, без предупреждения. Но можно утверждать, что во втором примере должен быть неопределённый, следовательно, FALSE, результат. Поэтому для него выдаётся предупреждение.

Таким образом, предупреждение выдаётся во многих случаях меньше, чем в более ранних версиях Perl, и только тогда, когда результат может быть спорным. Оказалось, что ни одна из оптимизаций, сделанных Perl (или которые, скорее всего, будут сделаны), не приводит к пропуску предупреждения, так что это решает обе проблемы более раннего подхода Perl. Наиболее часто используемое свойство, которое затронуто этим изменением, это \p{Unassigned}, являющееся сокращённой формой \p{General_Category=Unassigned}. Начиная с версии 5.20, все символы без Юникода считаются Unassigned. В более ранних выпусках совпадения не удавались, потому что результат считался неопределённым.

Единственное место, где предупреждение не выдаётся, когда оно должно было бы быть, — это если оптимизации приводят к тому, что сопоставление всего шаблона даже не пытается быть произведено. Например, Perl может выяснить, что для строки, чтобы соответствовать определённому шаблону регулярного выражения, строка должна содержать подстроку "foobar". Прежде чем начать сопоставление, Perl может искать эту подстроку, и если она не найдена, немедленно отклоняет сопоставление, не пытаясь его выполнить; таким образом, не генерируется ни одно предупреждение, даже если строка содержит символ с кодом выше Юникода.

Это поведение более «Делай то, что я имею в виду», чем в более ранних версиях Perl для большинства применений. Но оно выявляет меньше проблем для кода, который должен быть строго совместим с Юникодом. Поэтому доступен дополнительный режим работы для поддержки такого кода. Этот режим активируется, если шаблон регулярного выражения компилируется в лексическом пространстве, где класс предупреждений "non_unicode" был сделан фатальным, скажем, путём:

use warnings FATAL => "non_unicode"

(см. warnings). В этом режиме работы Perl будет выводить предупреждение для всех сопоставлений с символом без Юникода (не только спорных), и он пропустит оптимизации, которые могут привести к тому, что предупреждение не будет выведено. (В настоящее время он по-прежнему не будет предупреждать, если сопоставление даже не пытается быть произведено, как в примере "foobar" выше.)

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

Есть одно исключение из всего этого. \p{All} выглядит как свойство Юникода, но это расширение Perl, определённое как истинное для всех возможных символов, Юникод или нет, поэтому предупреждение никогда не генерируется при сопоставлении этого с символом без Юникода. (До версии 5.20 он был точным синонимом \p{Any}, сопоставляя символы 0 до 0x10FFFF.)

Безопасность при использовании Юникода

Сначала прочтите Рекомендации по безопасности Unicode.

Также обратите внимание на следующее:

  • Неправильно сформированный UTF-8

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

    Каждый символ может быть представлен более чем одной допустимой последовательностью UTF-8. Раньше и Юникод, и Perl считали все эти последовательности допустимыми, но теперь все последовательности, длиннее кратчайшей возможной, считаются неправильно сформированными.

    Юникод считает многие символы недопустимыми или избегаемыми. Perl обычно принимает их, после того, как они пройдут через любые фильтры ввода, которые могут попытаться их исключить. Об этом говорилось выше (см. «Суппорта» в UTF-16 в "Кодировки Юникода", "Символы без символа" и "За пределами символов Юникода").

  • Сопоставление шаблонов регулярных выражений может удивить вас, если вы не знакомы с Юникодом. Начиная с Perl 5.14, доступны несколько модификаторов шаблонов для управления этим, называемых модификаторами набора символов. Подробности приведены в "Модификаторы набора символов" в perlre.

Как обсуждалось ранее, Perl имеет одну ногу (две копыта?) на каждой из двух планет: старом мире ASCII и однобайтовых локалей и новом мире Юникода, обновляя при необходимости. Если ваш устаревший код явно не использует Юникод, никакого автоматического переключения на Юникод не должно происходить.

Юникод в Perl на платформах EBCDIC

Юникод поддерживается на платформах EBCDIC. См. perlebcdic.

Если не обсуждаются конкретные проблемы с ASCII против EBCDIC, ссылки на кодировку UTF-8 в этом документе и в других местах следует понимать как имеющие в виду UTF-EBCDIC на платформах EBCDIC. См. "Юникод и UTF" в perlebcdic.

Поскольку UTF-EBCDIC очень похож на UTF-8, различия в основном скрыты от вас; use utf8 (а НЕ что-то вроде use utfebcdic) объявляет, что сценарий находится в «родной» 8-битной кодировке Юникода платформы. (Аналогично для слоя ":utf8".)

Локали

См. "Юникод и UTF-8" в perllocale

Когда Юникод не используется

Есть ещё много мест, где Юникод (в той или иной кодировке) мог бы быть передан в качестве аргументов или получен в качестве результатов, или то и другое в Perl, но этого не происходит, несмотря на то, что Perl имеет обширные возможности для ввода и вывода в Юникоде и некоторые другие «точки входа», такие как массив @ARGV (который иногда может интерпретироваться как UTF-8).

Вот такие интерфейсы. Также см. "Ошибка Юникода". Для всех этих интерфейсов Perl в настоящее время (по состоянию на v5.16.0) просто предполагает строки байтов как аргументы и результаты или строки UTF-8, если используется (устаревшая) директива encoding.

Одна из причин, по которой Perl не пытается определить роль Юникода в этих ситуациях, заключается в том, что ответы сильно зависят от операционной системы и файловой системы(ы). Например, возможность того, что имена файлов могут быть в Юникоде и в какой именно кодировке, — это не совсем понятие портативности. Точно так же для qx и system: насколько хорошо «интерфейс командной строки» (и какой из них?) справится с Юникодом?

  • chdir, chmod, chown, chroot, exec, link, lstat, mkdir, rename, rmdir, stat, symlink, truncate, unlink, utime, -X

  • %ENV

  • glob (также известный как <*>)

  • open, opendir, sysopen

  • qx (также известный как оператор обратных кавычек), system

  • readdir, readlink

«Ошибка Юникода»

Термин «ошибка Юникода» был применён к несоответствию символов в блоке Latin-1 Supplement, то есть между 128 и 255. Без указанной локали, в отличие от всех других символов или кодов, эти символы могут иметь очень разные семантики в зависимости от применяемых правил. (Символы с кодами выше 255 принуждают к правилам Юникода; в то время как правила для символов ASCII одинаковы как в ASCII, так и в правилах Юникода.)

В соответствии с правилами Юникода, эти символы верхнего латинского языка интерпретируются как символы Юникода, что означает, что они имеют ту же семантику, что и латинские (ISO-8859-1) и управляющие символы C1.

Как объясняется в "Правила ASCII против правил Юникода", в соответствии с правилами ASCII они рассматриваются как неназначенные символы.

Это может привести к неожиданным результатам. Например, семантика строки может внезапно измениться, если к ней добавлен символ с кодом выше 255, что меняет правила с ASCII на Юникод. В качестве примера рассмотрим следующую программу и её вывод:

$ perl -le'
    no feature "unicode_strings";
    $s1 = "\xC2";
    $s2 = "\x{2660}";
    for ($s1, $s2, $s1.$s2) {
        print /\w/ || 0;
    }
'
0
0
1

Если нет \w в s1 и не в s2, то почему их конкатенация имеет одну?

Эта аномалия происходит от попытки Perl не нарушать старые программы, которые не использовали Юникод, а также от желания Perl добавить поддержку Юникода без проблем. Но результат оказался не бесшовным. (Кстати, вы можете выбрать предупреждение, когда происходят такие вещи. См. encoding::warnings.)

use feature 'unicode_strings' была добавлена, начиная с Perl v5.12, для решения этой проблемы. Она влияет на эти вещи:

  • Изменение регистра скаляра, то есть использование uc(), ucfirst(), lc(), и lcfirst(), или \L, \U, \u и \l в контексте двойных кавычек, таких как подстановки в регулярных выражениях.

    В unicode_strings начиная с Perl 5.12.0, обычно используются правила Юникода. См. "lc" в perlfunc для получения подробностей о том, как это работает в сочетании с различными другими прагмами.

  • Использование сопоставления регулярных выражений без учета регистра (/i).

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

  • Сопоставление любого из нескольких свойств в регулярных выражениях.

    Эти свойства — \b (без фигурных скобок), \B (без фигурных скобок), \s, \S, \w, \W, и все классы символов Posix, *кроме* [[:ascii:]].

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

  • В quotemeta или его эквиваленте встроены \Q.

    Начиная с Perl 5.16.0, в рамках unicode_strings используются согласованные правила цитирования, как описано в "quotemeta" в perlfunc. До этого, или вне его области действия, никакие кодовые точки выше 127 не цитируются в строках, закодированных в UTF-8, но в строках, закодированных в байтах, кодовые точки от 128 до 255 всегда цитируются.

  • В операторе .. или диапазона.

    Начиная с Perl 5.26.0, оператор диапазона для строк последовательно обрабатывает их длину в рамках unicode_strings. До этого, или вне его области действия, он мог создавать строки, длина которых в символах превышала длину правой части, где правая часть занимала больше байтов, чем правильная конечная точка диапазона.

  • В split's специальном случае разделения по пробелам.

    Начиная с Perl 5.28.0, функция split с образцом, указанным в строке, содержащей единственный пробел, последовательно обрабатывает пробельные символы в рамках unicode_strings. До этого, или вне его области действия, символы, являющиеся пробелами в соответствии с правилами Юникода, но не в соответствии с правилами ASCII, обрабатывались как содержимое поля, а не разделители полей, когда они встречались в строках, закодированных в байтах.

Из вышеизложенного видно, что влияние unicode_strings увеличивалось с несколькими версиями Perl. (И поддержка Юникода в Perl продолжает совершенствоваться; лучше всего использовать последнюю доступную версию, чтобы получить наиболее полные и точные результаты.) Обратите внимание, что unicode_strings автоматически выбирается, если вы use 5.012 или выше.

Для версий Perl, предшествующих описанным выше, или когда строка передается функции вне области действия unicode_strings, см. следующий раздел.

Принудительное использование Юникода в Perl (или отмена принудительного использования Юникода в Perl)

Иногда (см. "Когда Юникод не используется" или "Ошибка «Юникод»") существуют ситуации, когда вам просто необходимо принудительно преобразовать строку байтов в UTF-8 или наоборот. Для этого можно использовать стандартный модуль Encode или низкоуровневые вызовы utf8::upgrade($bytestring) и utf8::downgrade($utf8string[, FAIL_OK]).

Обратите внимание, что utf8::downgrade() может завершиться неудачей, если строка содержит символы, которые не помещаются в байт.

Вызов любой из этих функций для строки, которая уже находится в нужном состоянии, является бесполезной операцией.

"Правила ASCII против правил Юникода" приводит все способы того, как строке дается возможность использовать правила Юникода.

Использование Юникода в XS

См. "Поддержка Юникода" в perlguts для ознакомления с Юникодом на уровне XS и "Поддержка Юникода" в perlapi для получения API-сведений.

Модификация Perl для работы с более ранними версиями Юникода (только для очень опытных пользователей)

Perl по умолчанию поставляется с последней поддерживаемой версией Юникода, встроенной, но цель состоит в том, чтобы позволить вам изменить ее на любую более раннюю версию. Однако в Perl v5.20 и v5.22 самая ранняя поддерживаемая версия — Юникод 5.1. Perl v5.18 и v5.24 способны обрабатывать все более ранние версии.

Загрузите файлы нужной версии Юникода с веб-сайта Unicode http://www.unicode.org. Эти файлы должны заменить существующие файлы в lib/unicore в дереве исходного кода Perl. Следуйте инструкциям в README.perl в этой директории, чтобы изменить некоторые из их названий, а затем постройте perl (см. INSTALL).

Портирование кода из perl-5.6.X

Начиная с версии 5.8, в Perl используется другая модель Юникода, чем в версии 5.6. В версии 5.6 программисту требовалось использовать прагму utf8, чтобы объявить, что данная область действия ожидает обработки данных Юникода, и нужно было убедиться, что в эту область действия попадают только данные Юникода. Если у вас есть код, работающий с версией 5.6, вам потребуются некоторые из следующих изменений в вашем коде. Примеры написаны так, что код будет продолжать работать в версии 5.6, поэтому вы можете смело их опробовать.

  • Дескриптор файла, который должен читать или записывать UTF-8

    if ($] > 5.008) {
      binmode $fh, ":encoding(UTF-8)";
    }
  • Скаляр, который будет передан некоторому расширению

    Будь то Compress::Zlib, Apache::Request или любое расширение, в котором нет упоминания о Юникоде в справке, вам нужно убедиться, что флаг UTF8 снят. Обратите внимание, что на момент написания этого документа (январь 2012 г.) упомянутые модули не поддерживают UTF-8. Проверьте документацию, чтобы убедиться, что это по-прежнему актуально.

    if ($] > 5.008) {
      require Encode;
      $val = Encode::encode("UTF-8", $val); # make octets
    }
  • Скаляр, полученный обратно от расширения

    Если вы считаете, что скаляр возвращается в формате UTF-8, вы, скорее всего, захотите восстановить флаг UTF8:

    if ($] > 5.008) {
      require Encode;
      $val = Encode::decode("UTF-8", $val);
    }
  • То же самое, если вы уверены, что это UTF-8

    if ($] > 5.008) {
      require Encode;
      Encode::_utf8_on($val);
    }
  • Обёртка для DBI fetchrow_array и fetchrow_hashref

    Если база данных содержит только UTF-8, функция-обёртка или метод — удобный способ заменить все ваши вызовы fetchrow_array и fetchrow_hashref. Функция-обёртка также упростит адаптацию к будущим улучшениям в вашем драйвере базы данных. Обратите внимание, что на момент написания этого документа (январь 2012 г.) в DBI нет стандартизированного способа обработки данных UTF-8. Проверьте документацию DBI, чтобы убедиться, что это по-прежнему актуально.

    sub fetchrow {
      # $what is one of fetchrow_{array,hashref}
      my($self, $sth, $what) = @_;
      if ($] < 5.008) {
        return $sth->$what;
      } else {
        require Encode;
        if (wantarray) {
          my @arr = $sth->$what;
          for (@arr) {
            defined && /[^\000-\177]/ && Encode::_utf8_on($_);
          }
          return @arr;
        } else {
          my $ret = $sth->$what;
          if (ref $ret) {
            for my $k (keys %$ret) {
              defined
              && /[^\000-\177]/
              && Encode::_utf8_on($_) for $ret->{$k};
            }
            return $ret;
          } else {
            defined && /[^\000-\177]/ && Encode::_utf8_on($_) for $ret;
            return $ret;
          }
        }
      }
    }
  • Большой скаляр, который, как вы знаете, может содержать только ASCII

    Скаляры, содержащие только ASCII и помеченные как UTF-8, иногда задерживают работу вашей программы. Если вы распознаёте такую ситуацию, просто снимите флаг UTF8:

    utf8::downgrade($val) if $] > 5.008;

ОШИБКИ

См. также "Ошибка «Юникод»" выше.

Взаимодействие с расширениями

При обмене данными между Perl и расширением расширение должно уметь понимать флаг UTF8 и действовать соответствующим образом. Если расширение не распознаёт этот флаг, вероятно, оно вернёт данные с неправильным флагом.

Поэтому, если вы работаете с данными Юникода, обратитесь к документации каждого используемого модуля, если там есть какие-либо вопросы о взаимодействии с данными Юникода. Если в документации вообще ничего не сказано о Юникоде, предполагайте худшее и, вероятно, посмотрите на исходный код, чтобы узнать, как реализован модуль. Модули, написанные полностью на Perl, не должны вызывать проблем. Модули, которые напрямую или косвенно обращаются к коду, написанному на других языках программирования, подвержены риску.

Для затронутых функций простым способом избежать повреждения данных является явное указание кодировки обмениваемых данных. Выберите кодировку, которую расширение может обработать. Преобразуйте аргументы, передаваемые расширениям, в эту кодировку, а результаты — обратно в внутреннее представление Perl, как показано ниже:

sub my_escape_html ($) {
    my($what) = shift;
    return unless defined $what;
    Encode::decode("UTF-8", Foo::Bar::escape_html(
                                 Encode::encode("UTF-8", $what)));
}

Иногда, когда расширение не преобразует данные, а только хранит и извлекает их, вы сможете использовать, в противном случае, опасную функцию Encode::_utf8_on(). Допустим, популярное расширение Foo::Bar, написанное на C, предоставляет метод param, который позволяет хранить и извлекать данные в соответствии с такими прототипами:

$self->param($name, $value);            # set a scalar
$value = $self->param($name);           # retrieve a scalar

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

sub param {
  my($self,$name,$value) = @_;
  utf8::upgrade($name);     # make sure it is UTF-8 encoded
  if (defined $value) {
    utf8::upgrade($value);  # make sure it is UTF-8 encoded
    return $self->SUPER::param($name,$value);
  } else {
    my $ret = $self->SUPER::param($name);
    Encode::_utf8_on($ret); # we know, it is UTF-8 encoded
    return $ret;
  }
}

Некоторые расширения предоставляют фильтры на входах/выходах данных, такие как DB_File::filter_store_key и т. п. Обращайте внимание на такие фильтры в документации ваших расширений; они могут значительно облегчить переход к обработке данных Юникода.

Скорость

Некоторые функции медленнее при работе со строками, закодированными в UTF-8, чем со строками, закодированными в байтах. Все функции, которые должны переходить между символами, такими как length(), substr() или index(), или сопоставление регулярных выражений, могут работать намного быстрее, когда основные данные закодированы в байтах.

В Perl 5.8.0 замедление часто было довольно заметным; в Perl 5.8.1 была введена схема кэширования, которая улучшила ситуацию. В целом, операции со строками, закодированными в UTF-8, всё ещё медленнее. Например, свойства Юникода (классы символов), такие как \p{Nd}, известны тем, что они значительно медленнее (в 5–20 раз) своих более простых аналогов, таких как [0-9] (конечно, существуют сотни символов Юникода, соответствующих Nd, по сравнению с 10 символами ASCII, соответствующими [0-9]).

См. также

perlunitut, perluniintro, perluniprops, Encode, open, utf8, bytes, perlretut, "${^UNICODE}" в perlvar, http://www.unicode.org/reports/tr44).

© 1993–2020 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.28.3/perlunicode

Spec-Zone.ru

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