Максимальное значение Hex в регулярном выражении

Без использования u используется шестиугольный диапазон, который можно использовать [\x{00}-\x{ff}] , но с флагом u он достигает 4-байтового значения \x{7fffffff} ( [\x{00000000}-\x{7fffffff}] ).

Поэтому, если я выполнил приведенный ниже код:

 preg_match("/[\x{00000000}-\x{80000000}]+/u", $str, $match); 

Получит эту ошибку :

 Warning: preg_match(): Compilation failed: character value in \x{...} sequence is too large 

Поэтому я не могу сопоставить букву типа 𡃁 с эквивалентным шестнадцатеричным значением f0 a1 83 81 . Вопрос заключается не в том, как сопоставить эти буквы, а о том, как этот диапазон и эта граница исходила из того, что модификатор u должен обрабатывать строки как UTF-16

PCRE поддерживает UTF-16 с v8.30

 echo PCRE_VERSION; 

Версия PCRE с PHP 5.3.24 – 5.3.28, 5.4.14 – 5.5.7:

 8.32 2012-11-30 

Версия PCRE с PHP 5.3.19 – 5.3.23, 5.4.9 – 5.4.13:

 8.31 2012-07-06 

http://3v4l.org/CrPZ8

Поэтому я не могу сопоставить букву, подобную 𡃁 с эквивалентным шестнадцатеричным значением f0 a1 83 81. Вопрос заключается не в том, как сопоставить эти буквы, а в том, как этот диапазон и эта граница исходила из того, что модификатор u должен обрабатывать строки как UTF-16

Вы смешиваете две концепции, которые вызывают эту путаницу.

F0 A1 83 81 не является шестнадцатеричным значением символа 𡃁. Так UTF-8 кодирует кодовую точку для этого символа в потоке байтов.

Верно, что PHP поддерживает кодовые точки UTF-16 для шаблона \x{} , но значения внутри { и } представляют собой кодовые точки UTF-16, а не фактические байты, используемые для кодирования заданного символа в потоке байтов.

Таким образом, наибольшее возможное значение, которое вы можете использовать с \x{} на самом деле 10FFFF .

И для соответствия 𡃁 с PHP вам нужно использовать его кодовую точку, которая, как было предложено @minitech в его комментарии, – \x{0210c1} .

Дальнейшее объяснение, приведенное в разделе «Действительность строк» из документации PCRE .

Вся строка проверяется перед любой другой обработкой. В дополнение к проверке формата строки проверяется, чтобы все кодовые точки лежали в диапазоне U + 0 до U + 10FFFF, исключая суррогатную область. Так называемые «несимвольные» кодовые точки не исключаются, поскольку в Unicode Corrigendum # 9 четко указано, что их не должно быть.

Символы в «Surrogate Area» Unicode зарезервированы для использования UTF-16, где они используются парами для кодирования кодовых точек со значениями, превышающими 0xFFFF. Кодовые точки, которые кодируются парами UTF-16, доступны независимо в кодировках UTF-8 и UTF-32. (Другими словами, вся суррогатная вещь – это выдумка для UTF-16, которая, к сожалению, запутывает UTF-8 и UTF-32.)

Unicode и UTF-8, UTF-16, UTF-32

Unicode – это набор символов, который определяет сопоставление от символов к кодовым точкам, а кодировки символов (UTF-8, UTF-16, UTF-32) определяют, как хранить кодовые точки Юникода.

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

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

Используя пример в вопросе, 𡃁 сопоставляется с кодовой точкой U+210C1 , но он может быть закодирован как F0 A1 83 81 в UTF-8, D844 DCC1 в UTF-16 и 000210C1 в UTF-32.

Чтобы быть точным, приведенный выше пример показывает, как сопоставить кодовую точку с кодовыми единицами (форму кодировки символов). То, как модули кода сопоставляются с октетной последовательностью, – другое дело. См. Модель кодировки Unicode

8-разрядная, 16-разрядная и 32-разрядная библиотека PCRE

Поскольку PHP еще не принял PCRE2 (версия 10.10), цитируемый текст из документации оригинального PCRE.

Поддержка 16-битной и 32-битной библиотеки

PCRE включает поддержку 16-разрядной строки в версии 8.30 и 32-разрядную строку из версии 8.32, в дополнение к 8-битной библиотеке по умолчанию.

Помимо поддержки 8-разрядных символьных строк, PCRE также поддерживает 16-разрядные строки (начиная с версии 8.30) и 32-разрядные строки (из версии 8.32) с помощью двух дополнительных библиотек. Они могут быть построены, а также, а не 8-битная библиотека. […]

Значение 8-битного, 16-битного, 32-битного

8-битный, 16-битный и 32-битный здесь относится к блоку данных (блок кода).

Ссылки на байты и UTF-8 в этом документе следует читать как ссылки на 16-битные единицы данных и UTF-16 при использовании 16-битной библиотеки или 32-битных блоков данных и UTF-32 при использовании 32-битной библиотеки , если не указано иное. Более подробная информация о конкретных отличиях для 16-разрядных и 32-разрядных библиотек приведена на страницах pcre16 и pcre32.

Это означает, что 8-битная / 16-разрядная / 32-битная библиотека ожидает, что шаблон и входная строка будут последовательностями 8-разрядных / 16-разрядных / 32-битных блоков данных или действительными UTF-8 / UTF-16 / UTF-32.

Различные API-интерфейсы для различной ширины блока данных

PCRE предоставляет 3 набора идентичных API для 8-битных, 16-битных и 32-битных библиотек, дифференцированных префиксом ( pcre_ , pcre16_ и pcre_32 соответственно).

16-битные и 32-битные функции работают так же, как и их 8-битные аналоги; они просто используют разные типы данных для своих аргументов и результатов, а их имена начинаются с pcre16_ или pcre32_ вместо pcre_ . Для каждой опции, которая имеет UTF8 в своем имени (например, PCRE_UTF8 ), имеются соответствующие 16-битные и 32-битные имена с заменой UTF8 на UTF16 или UTF32 соответственно. Этот объект на самом деле просто косметический; 16-битные и 32-битные имена опций определяют одинаковые значения бит.

В PCRE2 используется аналогичное соглашение об именовании функций , где 8-битная / 16-разрядная / 32-битная функция имеет _8 , _16 , _32 соответственно. Приложения, использующие только одну ширину блока кода, могут определять PCRE2_CODE_UNIT_WIDTH для использования общего имени функции без суффикса.

Режим UTF против режима без UTF

Когда UTF-режим установлен (с помощью опций в виде шаблона (*UTF) , (*UTF8) , (*UTF16) , (*UTF32) 1 или параметров компиляции PCRE_UTF8 , PCRE_UTF16 , PCRE_UTF32 ), все последовательности блоков данных интерпретируются как последовательности символов Юникода, которые состоят из всех кодовых точек от U + 0000 до U + 10FFFF, за исключением суррогатов и спецификации.

1 Параметры в шаблоне (*UTF8) , (*UTF16) , (*UTF32) доступны только в соответствующей библиотеке. Вы не можете использовать (*UTF16) в 8-битной библиотеке или любую несогласованную комбинацию, поскольку это просто не имеет смысла. (*UTF) доступен во всех библиотеках и предоставляет переносимый способ указания режима UTF в шаблоне.

В режиме UTF шаблон (который является последовательностью блоков данных) интерпретируется и проверяется как последовательность кодовых точек Unicode, декодируя последовательность как данные UTF-8 / UTF-16 / UTF-32 (в зависимости от используемого API) , прежде чем он будет скомпилирован. Строка ввода также интерпретируется и опционально проверяется как последовательность кодовых точек Unicode во время процесса сопоставления. В этом режиме класс символов соответствует одной допустимой кодовой точке Юникода.

С другой стороны , когда режим UTF не установлен (режим без UTF), все операции непосредственно работают с последовательностями блоков данных. В этом режиме класс символов соответствует одному блоку данных и за исключением максимального значения, которое может быть сохранено в одном блоке данных, нет ограничений на значение единицы данных. Этот режим может использоваться для сопоставления структуры в двоичных данных. Однако не используйте этот режим, когда вы имеете дело с символом Unicode , ну, если вы не в порядке с ASCII и игнорируете остальные языки.

Ограничения на значения символов

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

 8-bit non-UTF mode less than 0x100 8-bit UTF-8 mode less than 0x10ffff and a valid codepoint 16-bit non-UTF mode less than 0x10000 16-bit UTF-16 mode less than 0x10ffff and a valid codepoint 32-bit non-UTF mode less than 0x100000000 32-bit UTF-32 mode less than 0x10ffff and a valid codepoint 

Недопустимыми кодовыми точками Unicode являются диапазон от 0xd800 до 0xdfff (так называемые «суррогатные» кодовые точки) и 0xffef.

PHP и PCRE

Функции PCRE на PHP реализуются оболочкой, которая переводит флагов и вызовы PHP в API PCRE (как видно из ветки PHP 5.6.10).

Исходный код вызывается в 8-битный API-интерфейс PCRE ( pcre_ ), поэтому любая строка, переданная в функцию preg_ интерпретируется как последовательность 8-разрядных блоков данных (байты). Поэтому, даже если построены 16-разрядные и 32-разрядные библиотеки PCRE, они не доступны через API на стороне PHP вообще.

В результате функции PCRE в PHP ожидают:

  • … массив байтов в режиме, отличном от UTF (по умолчанию), который библиотека читает в 8-битных «символах» и компилирует для соответствия строкам 8-разрядных «символов».
  • … массив байтов, который содержит кодировку Unicode UTF-8, которую библиотека читает в символах Unicode и компилирует, чтобы соответствовать строкам Unicode UTF-8.

Это объясняет поведение, наблюдаемое в вопросе:

  • В режиме без UTF (без флага u ) максимальное значение в шестнадцатеричной escape-последовательности регулярного выражения представляет собой FF (как показано в [\x{00}-\x{ff}] )
  • В режиме UTF любое значение, выходящее за пределы 0x10ffff (например, \x{7fffffff} ) в шестнадцатеричной escape-последовательности регулярного выражения, просто не имеет смысла.

Пример кода

Этот пример кода демонстрирует:

  • Строки PHP – это просто массивы байтов и ничего не понимают в кодировке.
  • Различия между режимами UTF и не-UTF в функции PCRE.
  • Вызов функций PCRE в 8-битную библиотеку
 // NOTE: Save this file as UTF-8 // Take note of double-quoted string literal, which supports escape sequence and variable expansion // The code won't work correctly with single-quoted string literal, which has restrictive escape syntax // Read more at: https://php.net/language.types.string $str_1 = "\xf0\xa1\x83\x81\xf0\xa1\x83\x81"; $str_2 = "𡃁𡃁"; $str_3 = "\xf0\xa1\x83\x81\x81\x81\x81\x81\x81"; echo ($str_1 === $str_2)."\n"; var_dump($str_3); // Test 1a $match = null; preg_match("/\xf0\xa1\x83\x81+/", $str_1, $match); print_r($match); // Only match 𡃁 // Test 1b $match = null; preg_match("/\xf0\xa1\x83\x81+/", $str_2, $match); print_r($match); // Only match 𡃁 (same as 1a) // Test 1c $match = null; preg_match("/\xf0\xa1\x83\x81+/", $str_3, $match); print_r($match); // Match 𡃁 and the five bytes of 0x81 // Test 2a $match = null; preg_match("/𡃁+/", $str_1, $match); print_r($match); // Only match 𡃁 (same as 1a) // Test 2b $match = null; preg_match("/𡃁+/", $str_2, $match); print_r($match); // Only match 𡃁 (same as 1b and 2a) // Test 2c $match = null; preg_match("/𡃁+/", $str_3, $match); print_r($match); // Match 𡃁 and the five bytes of 0x81 (same as 1c) // Test 3a $match = null; preg_match("/\xf0\xa1\x83\x81+/u", $str_1, $match); print_r($match); // Match two 𡃁 // Test 3b $match = null; preg_match("/\xf0\xa1\x83\x81+/u", $str_2, $match); print_r($match); // Match two 𡃁 (same as 3a) // Test 4a $match = null; preg_match("/𡃁+/u", $str_1, $match); print_r($match); // Match two 𡃁 (same as 3a) // Test 4b $match = null; preg_match("/𡃁+/u", $str_2, $match); print_r($match); // Match two 𡃁 (same as 3b and 4a) 

Поскольку строки PHP представляют собой просто массив байтов, если файл сохранен правильно в некоторой кодировке, совместимой с ASCII, PHP просто с радостью прочитает байты, не заботясь о том, какая кодировка была изначально. Программист несет полную ответственность за кодирование и правильно декодировать строки.

  • То, что каждый программист абсолютно, положительно должен знать о кодировках и наборах символов для работы с текстом (раздел «Использование и злоупотребление обработкой PHP-кодировками»)
  • Наборы символов / проблемы кодирования символов
  • Часто задаваемые вопросы о PHP Charset – Какую кодировку я должен использовать для своих исходных файлов?

Из-за вышеуказанной причины, если вы сохраните файл выше в кодировке UTF-8, вы увидите, что $str_1 и $str_2 – это $str_2 и та же строка. $str_1 декодирует из escape-последовательности, а $str_2 читается дословно из исходного кода. В результате "/\xf0\xa1\x83\x81+/u" и "/𡃁+/u" являются одной и той же строкой внизу (также случай для "/\xf0\xa1\x83\x81+/" и "/𡃁+/" ).

Разница между режимом UTF и режимом без UTF четко показана в приведенном выше примере:

  • "/𡃁+/" рассматривается как последовательность символов F0 A1 83 81 2B где «символ» – один байт. Поэтому полученное регулярное выражение совпадает с последовательностью F0 A1 83 за которой следует один байт 81 повторяющий один или несколько раз.
  • "/𡃁+/u" проверяется и интерпретируется как последовательность символов UTF-8 U+210C1 U+002B . Следовательно, полученное регулярное выражение соответствует кодовой точке U+210C1 повторяемой один или несколько раз в строке UTF-8.

Соответствие символу Unicode

Если на входе нет других двоичных данных, настоятельно рекомендуется всегда включать режим u . Шаблон имеет доступ ко всем средствам для правильного соответствия символов Юникода, и как вход, так и шаблон проверяются как действительные строки UTF.

Опять же, используя пример 𡃁 , приведенный выше пример показывает два способа задания регулярного выражения:

 "/\xf0\xa1\x83\x81+/u" "/𡃁+/u" 

Первый метод не работает с одиночной кавычкой – поскольку escape-последовательность \x не распознается в одинарной \xf0\xa1\x83\x81+ , библиотека получит строку \xf0\xa1\x83\x81+ , которая в сочетании с режимом UTF будет соответствовать U+00F0 U+00A1 U+0083 а затем U+0081 повторяется один или несколько раз. Кроме того, это также путает следующего человека, читающего код: как они должны знать, что один символ Unicode повторяется один или несколько раз?

Второй метод работает хорошо, и его можно даже использовать с одной кавычкой, но вам нужно сохранить файл в кодировке UTF-8, особенно в случае с символами типа ÿ , поскольку символ также действителен в однобайтовой кодировке. Этот метод является опцией, если вы хотите совместить один символ или последовательность символов. Однако, как конечные точки диапазона символов, может быть неясно, к чему вы пытаетесь соответствовать. Сравните az , AZ , 0-9 , א-ת , в отличие от 一-龥 (который соответствует большинству блоков CJK Unified Ideographs (4E00-9FFF), за исключением неназначенных кодовых точек в конце) или 一-十 (который является неправильная попытка сопоставить китайские символы для числа от 1 до 10).

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

 "/\x{210C1}/u" '/\x{210C1}/u' 

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

Как предполагает minitech в первом комментарии, вы должны использовать код – для этого символа это \x{210C1} . Это также закодированная форма в UTF-32. F0 AF AB BF – кодированная последовательность UTF-8 (см. http://www.unicode.org/cgi-bin/GetUnihanData.pl?codepoint=210C1 ).

Существуют несколько версий PCRE, где вы можете использовать значения до \x{7FFFFFFF} . Но я действительно не знаю, что с ним можно сравнить.

Процитировать http://www.pcre.org/pcre.txt :

В режиме UTF-16 код символа является Unicode в диапазоне от 0 до 0x10ffff , за исключением значений в диапазоне от 0xd800 до 0xdfff, поскольку они являются «суррогатными» значениями, которые используются парами для кодирования значений больше 0xffff.

[…]

В режиме UTF-32 код символа является Unicode в диапазоне от 0 до 0x10ffff , за исключением значений в диапазоне от 0xd800 до 0xdfff, поскольку они являются «суррогатными» значениями, которые плохо сформированы в UTF-32.

0x10ffff – это самое большое значение, которое вы можете использовать для сопоставления персонажа (вот что я извлекаю из этого). 0x10ffff в настоящее время также является самой большой точкой кода, определенной в стандарте Unicode (см. Раздел «Что такое различия между UTF?» ), Поэтому каждое значение выше не имеет никакого смысла (или я просто не получаю его) …

Я не уверен в php, но на кодовых точках на самом деле нет губернатора
поэтому не имеет значения, что есть только 1,1 миллиона действительных.
Это может быть изменено в любое время, но не на самом деле до двигателей
для обеспечения этого. Есть зарезервированные cp, которые являются отверстиями в допустимом диапазоне,
есть суррогаты в действующем диапазоне, причины для этого бесконечны
чтобы не было другого ограничения, кроме размера слова.

Для UTF-32 вы не можете переходить через 31 бит, потому что 32 – бит знака.
0x00000000 - 0x7FFFFFFF

Имеет смысл, поскольку unsigned int как тип данных является естественным размером 32-разрядных аппаратных регистров.

Для UTF-16, даже более верное, вы можете видеть одно и то же ограничение, маскируемое до 16 бит. Бит 32 по-прежнему является битом знака, оставляя 0x0000 - 0xFFFF допустимым диапазоном.

Обычно, если вы используете движок, поддерживающий ICU, вы сможете его использовать,
который преобразует как исходный, так и регулярный выражения в UTF-32. Boost Regex – один из таких движков.

редактировать:

Что касается UTF-16

Я думаю, когда Unicode перерос 16 бит, они пробивали отверстие в 16-битном диапазоне для суррогатных пар. Но он оставил только 20 полных битов между парой как пригодный для использования.

10 бит в каждом суррогате с другим 6, используемым для определения hi или lo.
Похоже, что это оставило людей Юникода с пределом 20 бит + дополнительный 0xFFFF округленный, в общей сложности 0x10FFFF кодов, с неиспользуемыми дырками.

Чтобы иметь возможность конвертировать в другую кодировку (8/16/32) все кодовые страницы
должен быть фактически конвертируемым. Таким образом, навсегда обратная совместимая 20-битная
ловушку, с которой они столкнулись раньше, но теперь должны жить.

Независимо от того, что регулярные выражения не будут применять этот предел в ближайшее время, возможно, никогда.
Что касается суррогатов, то они являются дырой , и неверно сформированный буквальный суррогат не может быть преобразован между режимами. Это просто относится к буквальному кодированному символу во время преобразования, а не к шестнадцатеричному представлению одного. Например, его легко найти текст в режиме UTF-16 (только) для непарных суррогатов или даже парных.

Но я думаю, что двигатели регулярных выражений действительно не заботятся о дырах или ограничениях, им все равно, в каком режиме находится строка темы. Нет, двигатель не собирается говорить:
«Привет, режим UTF-16. Я лучше конвертирую \x{210C1} в \x{D844}\x{DCC1} . Подождите, если я это сделаю, что мне делать, если его квантифицированные \x{210C1}+ , начинают вводить регулярные выражения вокруг него? Хуже того, что, если его в классе [\x{210C1}] ? Nah .. лучше ограничьте это \x{FFFF} .

Некоторые удобные денди, конверсии суррогатных псевдокодов, которые я использую:

  Definitions: ==================== 10-bits 3FF = 000000 1111111111 Hi Surrogate D800 = 110110 0000000000 DBFF = 110110 1111111111 Lo Surrogate DC00 = 110111 0000000000 DFFF = 110111 1111111111 Conversions: ==================== UTF-16 Surrogates to UTF-32 if ( TESTFOR_SURROGATE_PAIR(hi,lo) ) { u32Out = 0x10000 + ( ((hi & 0x3FF) << 10) | (lo & 0x3FF) ); } UTF-32 to UTF-16 Surrogates if ( u32In >= 0x10000) { u32In -= 0x10000; hi = (0xD800 + ((u32In & 0xFFC00) >> 10)); lo = (0xDC00 + (u32In & 0x3FF)); } Macro's: ==================== #define TESTFOR_SURROGATE_HI(hs) (((hs & 0xFC00)) == 0xD800 ) #define TESTFOR_SURROGATE_LO(ls) (((ls & 0xFC00)) == 0xDC00 ) #define TESTFOR_SURROGATE_PAIR(hs,ls) ( (((hs & 0xFC00)) == 0xD800) && (((ls & 0xFC00)) == 0xDC00) ) // #define PTR_TESTFOR_SURROGATE_HI(ptr) (((*ptr & 0xFC00)) == 0xD800 ) #define PTR_TESTFOR_SURROGATE_LO(ptr) (((*ptr & 0xFC00)) == 0xDC00 ) #define PTR_TESTFOR_SURROGATE_PAIR(ptr) ( (((*ptr & 0xFC00)) == 0xD800) && (((*(ptr+1) & 0xFC00)) == 0xDC00) ) 

«но хочу знать о max hex-границе в регулярном выражении»: * во всех utf-режимах: 0x10ffff * собственный 8-бит режим: 0xff * собственный 16-разрядный режим: 0xffff * собственный 32-разрядный режим: 0x1fffffff