Подтвердить что ты не робот

Максимальное значение 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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

Ответ 2

Кодировка Unicode и UTF-8, UTF-16, UTF-32

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

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

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

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

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

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

Так как 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), все последовательности блоков данных интерпретируются как последовательности символов Unicode, которые состоят из всех кодовых точек от 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), максимальное значение в шестнадцатеричной управляющей последовательности регулярного выражения представляет собой 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 просто с радостью прочитает байты, не заботясь о том, какая кодировка была изначально. Программист несет полную ответственность для правильной кодировки и декодирования строк.

Из-за вышеуказанной причины, если вы сохраните файл выше в кодировке UTF-8, вы увидите, что $str_1 и $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"

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

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

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

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

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

Ответ 3

Я не уверен в 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) )

Ответ 4

Как показывает 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?), таким образом, каждый значение выше не имеет никакого смысла (или я просто не понимаю)...

Ответ 5

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