Spec-Zone.ru › Perl 5.34

perlunicode

СОДЕРЖАНИЕ

  • ИМЯ
  • ОПИСАНИЕ
    • Важные замечания
    • Семантика байтов и символов
    • Правила ASCII по сравнению с правилами Unicode
    • Расширенные кластеры графем (логические символы)
    • Свойства символов Unicode
      • Общая категория
      • Типы символов с двунаправленной ориентацией
      • Шрифты
      • Использование префикса "Is"
      • Блоки
      • Другие свойства
    • Сравнение \N{...} и \p{name=...}
    • Подстановочные знаки в значениях свойств
    • Пользовательские свойства символов
    • Пользовательские преобразования регистра (только для серьезных хакеров)
    • Кодировки символов для ввода и вывода
    • Уровень поддержки 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 стремится унифицировать кодировки всех наборов символов мира в единый стандарт. Для многих кодировочных стандартов, существовавших на момент создания Unicode, преобразование из каждого в Unicode по существу означало добавление константы к каждой кодовой точке исходного стандарта, а преобразование обратно — просто вычитание этой же константы. Для ASCII и ISO-8859-1 константа равна 0. Для ISO-8859-5 (кириллица) константа равна 864; для иврита (ISO-8859-8) — 1488; тайского (ISO-8859-11) — 3424 и так далее. Это упростило преобразования и способствовало принятию Unicode.

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

Unicode — это всеобъемлющий стандарт. Он определяет множество вещей, выходящих за рамки Perl, например, как отображать последовательности символов. Для полного обсуждения всех аспектов Unicode см. https://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 были удалены с версии 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 без маркировки BOM любого порядка байтов, 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} эквивалентны. Все это называется «расслабленным соответствием» Юникодом. Свойство «имя» имеет некоторые ограничения на это из-за нескольких исключительных случаев. Полные подробности приведены в https://www.unicode.org/reports/tr44/tr44-24.html#UAX44-LM2.

Несколько мест, где используется более строгое соответствие, находятся посередине чисел, свойства «имя» и свойства расширений Perl, начинающиеся или заканчивающиеся подчеркиванием. Более строгое соответствие учитывает пробелы (за исключением случаев, прилегающих к не-буквенным символам), дефисы и не-внутренние подчеркивания.

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

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

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

Категория

Каждый символ Юникода имеет общую категорию, которая является «самой обычной категоризацией символа» (из https://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. А https://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": https://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": https://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 (в https://www.unicode.org/reports/tr18).

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

\p{All}

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

\p{Alnum}

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

\p{Any}

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

\p{ASCII}

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

\p{Assigned}

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

\p{Blank}

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

\p{Decomposition_Type: Non_Canonical} (Сокращенно: \p{Dt=NonCanon})

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

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

Обратите внимание, что у большинства символов Юникода нет разложения, поэтому их тип разложения "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: *} (Сокращенно: \p{In=*})

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

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

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

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

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

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

\p{Print}

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

\p{SpacePerl}

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

Мемоническое значение: пробел, измененный Perl. (В него не входит вертикальная табуляция до v5.18, которую как стандарт POSIX, так и Юникод считают пробелом.)

\p{Title} и \p{Titlecase}

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

\p{Unicode}

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

\p{VertSpace}

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

\p{Word}

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

\p{XPosix...}

Существует несколько таких соответствий, которые являются стандартными классами POSIX, расширенными до всего диапазона Юникода. Они описаны в "Классы символов POSIX" в perlrecharclass.

Сравнение \N{...} и \p{name=...}

Начиная с Perl 5.32, вы можете указать символ по его имени в шаблонах регулярных выражений с помощью \p{name=...}. Это дополнение к давнему методу использования \N{...}. Следующее резюмирует различия между этими двумя:

                      \N{...}       \p{Name=...}
can interpolate    only with eval       yes            [1]
custom names            yes             no             [2]
name aliases            yes             yes            [3]
named sequences         yes             yes            [4]
name value parsing     exact       Unicode loose       [5]
[1]

Возможность интерполяции означает, что вы можете сделать что-то вроде

qr/\p{na=latin capital letter $which}/

и указать $which в другом месте.

[2]

Вы можете создавать свои собственные имена для символов и перезаписывать официальные имена при использовании \N{...}. См. "CUSTOM ALIASES" в charnames.

[3]

Некоторые символы имеют несколько имен (синонимы).

[4]

Некоторым конкретным последовательностям символов дано одно имя в дополнение к их отдельным именам.

[5]

Сопоставление точных значений имен означает, что вам нужно точно указать регистр, дефисы, подчеркивания и пробелы в нужном имени. Слабое сопоставление следует правилам Юникода https://www.unicode.org/reports/tr44/tr44-24.html#UAX44-LM2, где это в основном не имеет значения. За исключением нескольких исключительных случаев с именами символов, эти правила такие же, как и для любого другого свойства \p{...}.

Подстановки в значениях свойств

Начиная с Perl 5.30, можно сделать что-то вроде этого:

qr!\p{numeric_value=/\A[0-5]\z/}!

или, сократив и добавив /x,

qr! \p{nv= /(?x) \A [0-5] \z / }!

Это соответствует всем кодовым точкам, числовое значение которых равно 0, 1, 2, 3, 4 или 5. Этот конкретный пример можно было бы вместо этого написать как

qr! \A [ \p{nv=0}\p{nv=1}\p{nv=2}\p{nv=3}\p{nv=4}\p{nv=5} ] \z !xx

в более ранних версиях Perl, поэтому в этом случае эта функция просто упрощает и сокращает запись. Если бы мы не включили \A и \z, эти соответствия включали бы такие вещи, как 1/2, так как она содержит 1 (а также 2). Как написано, она соответствует таким вещам, как индексы с этими числовыми значениями. Если мы хотели только десятичные цифры с этими числовыми значениями, мы могли бы сказать,

qr! (?[ \d & \p{nv=/[0-5]/ ]) }!x

\d избавляет от необходимости привязывать шаблон, так как она заставляет результат соответствовать только [0-9], а [0-5] дополнительно ограничивает его.

Текст в приведенных выше примерах, заключенный в "/" символы, может быть практически любым регулярным выражением. Она независима от основного шаблона, поэтому не разделяет никакие группы захвата и т. д. Разделители для нее должны быть знаками препинания ASCII, но они НЕ могут быть определены "{", ни "}", ни содержать литерную "}", так как она определяет конец окружающего \p{}. Как и любой шаблон, некоторые другие разделители завершаются их зеркальными отображениями. Это "(", "[", и "<". Если разделитель является одним из "-", "_", "+", или "\", или совпадает с разделителем, используемым для окружающего шаблона, он должен предваряться обратной косой чертой, как перед, так и после.

Остерегайтесь использования "$" для указания конца строки. Это слишком легко может быть интерпретировано как значок пунктуации, как $/.

Никакие модификаторы не должны следовать за заключительным разделителем. Вместо этого используйте "(?adlupimnsx-imnsx)" в perlre и/или "(?adluimnsx-imnsx:pattern)" в perlre для указания модификаторов. Однако некоторые модификаторы запрещены в вашем подмаске с подстановочными знаками. Единственным разрешенным модификатором набора символов является /aa; любые другие наборы символов, -m, p, и s запрещены. Указание таких модификаторов, как qr/.../gc, которые не разрешены в обозначении (?...), обычно вызывает предупреждение, но в случае подмасок с подстановочными знаками их использование является ошибкой. Модификатор m неэффективен; всё, что соответствует, будет на одной строке.

По умолчанию ваш шаблон сопоставляется без учета регистра, как если бы был указан /i. Вы можете изменить это, указав (?-i) в вашем шаблоне.

Также существуют некоторые запрещенные операции. Вы не можете вкладывать вызовы \p{...} и \P{...} внутри подмаски с подстановочными знаками, и \G не имеет смысла, поэтому также запрещено.

И квантификатор * (или его эквивалент (0,}) запрещен.

Эта функция недоступна, когда левая часть префиксна Is_, а также для любых форм, помеченных как "Не рекомендуется" в "Не рекомендуется" в perluniprops.

Эта экспериментальная функция добавлена для начала реализации https://www.unicode.org/reports/tr18/#Wildcard_Properties. Её использование вызовет предупреждение (по умолчанию включено) в категории experimental::uniprop_wildcards. Мы оставляем за собой право изменить её работу по мере накопления опыта.

Ваша подмаска может быть практически любой, но для её полезности она должна совпадать, когда вызывается либо с полным именем значения свойства с подчёркиваниями (и/или пробелами в свойстве блока) и некоторыми заглавными буквами, либо с значением свойства во всех строчных буквах с выведенными пробелами и подчёркиваниями. Например,

qr!\p{Blk=/Old I.*/}!
qr!\p{Blk=/oldi.*/}!

будет соответствовать тем же вещам.

Ещё один пример, который показывает, что внутри \p{...}, /x не требуется для наличия пробелов:

qr!\p{scx= /Hebrew|Greek/ }!

Для большей безопасности мы должны были бы закрепить вышеприведенный пример, чтобы предотвратить совпадения для чего-то вроде Hebrew_Braille, но пока нет каких-либо имён скриптов подобного рода. Если ни одно из допустимых значений для свойства не соответствует вашему шаблону, выдаётся предупреждение. Скорее всего, в будущей версии выведется предупреждение, если ваш шаблон приведет к совпадению с каждым возможным кодовым точкой.

Начиная с версии 5.32, свойства Имя, псевдонимы имени и именованные последовательности разрешены для сопоставления. Они считаются единым комбинированным свойством, как это давно делается для \N{}. Слабое соответствие не работает таким же образом для них, как для значений других свойств. Правила приведены в https://www.unicode.org/reports/tr44/tr44-24.html#UAX44-LM2. В результате Perl не пытается выполнить слабое соответствие для вас, как это происходит в других свойствах. Все буквы в именах заглавные, но вы можете добавить (?i) в свою подмаску, чтобы игнорировать регистр. Если вы не уверены, где пробел, вы можете использовать ? в своей подмаске. Ни одно имя символа не содержит подчёркивания, поэтому не пытайтесь найти его. Использование тире особенно проблематично; см. ссылку выше. Однако обратите внимание, что, начиная с Unicode 13.0, единственный активно используемый шрифт, имеющий странности с этим, — это тибетский; а также два корейских символа U+116C HANGUL JUNGSEONG OE и U+1180 HANGUL JUNGSEONG O-E. Unicode не гарантирует, что в будущем не будут добавлены имена с проблемой тире.

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

Пример использования подстановочных знаков свойства Имя:

qr!\p{name=/(SMILING|GRINNING) FACE/}!

Другой пример:

qr/(?[ \p{name=\/CJK\/} - \p{ideographic} ])/

что представляет собой около 200 (по состоянию на Unicode 13.0) символов CJK, которые не являются иероглифами.

Существуют определённые свойства, с которыми подмаски с подстановочными знаками в настоящее время не работают.

Bidi Mirroring Glyph
Bidi Paired Bracket
Case Folding
Decomposition Mapping
Equivalent Unified Ideograph
Lowercase Mapping
NFKC Case Fold
Titlecase Mapping
Uppercase Mapping

Также не реализована форма @unicode_property@.

Вот полный пример сопоставления адресов интернет-протокола IPV4 в любом (единственном) скрипте

no warnings 'experimental::regex_sets';
no warnings 'experimental::uniprop_wildcards';

# Can match a substring, so this intermediate regex needs to have
# context or anchoring in its final use.  Using nt=de yields decimal
# digits.  When specifying a subset of these, we must include \d to
# prevent things like U+00B2 SUPERSCRIPT TWO from matching
my $zero_through_255 =
 qr/ \b (*sr:                                  # All from same sript
           (?[ \p{nv=0} & \d ])*               # Optional leading zeros
       (                                       # Then one of:
                                 \d{1,2}       #   0 - 99
           | (?[ \p{nv=1} & \d ])  \d{2}       #   100 - 199
           | (?[ \p{nv=2} & \d ])
              (  (?[ \p{nv=:[0-4]:} & \d ]) \d #   200 - 249
               | (?[ \p{nv=5}     & \d ])
                 (?[ \p{nv=:[0-5]:} & \d ])    #   250 - 255
              )
       )
     )
   \b
 /x;

my $ipv4 = qr/ \A (*sr:         $zero_through_255
                        (?: [.] $zero_through_255 ) {3}
                  )
               \z
           /x;

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

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

# assuming property IsForeign 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, если сопоставление по регистру включено, и отличен от нуля, если сопоставление без учета регистра включено. Подпрограмма может возвращать различные значения в зависимости от значения флага, и один набор значений будет неизменно действовать для всех сопоставлений по регистру, а другой — для всех сопоставлений без учета регистра.

Обратите внимание, что если регулярное выражение является заражённым, 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, так как каждая из них не входит в Kana. Вы можете использовать пересечение для исключения их, если нужно, как показывает этот изменённый пример:

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 регулярные выражения», версия 18, октябрь 2016.

Уровень 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 поддерживает эти свойства. См. R2.7 для других свойств.
[3] Perl имеет \d \D \s \S \w \W \X [:prop:] [:^prop:], плюс все свойства, указанные по адресу https://www.unicode.org/reports/tr18/#Compatibility_Properties. Они описаны выше в разделе "Другие свойства"
[4]

Экспериментальная функция "(?[...])", начиная с версии 5.18, выполняет это.

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

  • Предварительный просмотр регулярного выражения

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

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

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

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

    Но в этом конкретном примере вы, вероятно, действительно хотите

    \p{Greek}

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

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

    Он реализует синтаксис группирования, пересечения, объединения и удаления (вычитания) 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 and  - Partial       [10]
        Character Classes with Strings
RL2.3   Default Word Boundaries         - Done          [11]
RL2.4   Default Case Conversion         - Done
RL2.5   Name Properties                 - Done
RL2.6   Wildcards in Property Values    - Partial       [12]
RL2.7   Full Properties                 - Partial       [13]
RL2.8   Optional Properties             - Partial       [14]
[9] Unicode переписал этот раздел UTS#18, сказав, что получение канонического соответствия (см. UAX#15 "Формы нормализации Unicode") в основном должно выполняться на уровне программиста. Используйте NFD для записи как ваших регулярных выражений, так и текста, для сравнения с ними (вы можете использовать Unicode::Normalize).
[10] Perl имеет \X и \b{gcb}. Unicode отозвали свой "режим кластера графем", и недавно добавили свойства строк, которые Perl пока не поддерживает.
[11] см. UAX#29 "Сегментация текста Unicode",
[12] см. "Подстановки в значениях свойств" выше.
[13] Perl поддерживает все свойства в базе данных символов Unicode (UCD). Он пока не поддерживает перечисленные свойства, полученные из других источников Unicode.
[14] Единственное необязательное свойство, которое поддерживает Perl, — это Имя последовательности. Ни одно из этих свойств не находится в UCD.

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

Это было отозвано Unicode.

Кодировки 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

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

В результате этих проблем, начиная с версии 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.)

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

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

Кроме того, обратите внимание на следующее:

  • Неправильно сформированный 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, так и в Юникоде.)

Согласно правилам Юникода, эти символы верхнего латинского набора интерпретируются как коды Юникода, что означает, что они имеют те же семантические значения, что и в Латин-1 (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

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

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

use feature 'unicode_strings' был добавлен, начиная с версии Perl 5.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 https://www.unicode.org. Они должны заменить существующие файлы в папке lib/unicore в дереве исходного кода Perl. Следуйте инструкциям в файле README.perl в этом каталоге, чтобы изменить некоторые их имена, а затем скомпилируйте perl (см. INSTALL).

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

Perl, начиная с версии 5.8, имеет другую модель Юникода, чем 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, не должны вызывать проблем. Модули, которые напрямую или косвенно обращаются к коду, написанному на других языках программирования, подвержены риску.

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

Например, предположим, что популярная функция Foo::Bar::escape_html ещё не поддерживает данные Юникода. Обёртковая функция преобразует аргумент в обычный UTF-8 и преобразует результат обратно в внутреннее представление 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, https://www.unicode.org/reports/tr44).

© 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.34.0/perlunicode

Spec-Zone.ru

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