Не-ASCII символы в регулярном выражении режима UTF-8

Вопрос

Несмотря на руководство по PHP:

«В режиме UTF-8 символы со значениями больше 128 не соответствуют ни одному из классов символов POSIX».

Почему персидские цифры совпадают \d или же [[:digit:]] в «режиме UTF-8»?

разработка

В замечании ответчика в несвязанный вопрос упоминается, что в регулярных выражениях \d не только соответствует ASCII цифрам 0 через 9 но также, например, персидские цифры (۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷).

Вышеупомянутый вопрос помечен но поведение можно наблюдать и в PHP. Имея это в виду, я написал следующий «тест»:

$string = 'I have ۳ apples and 5 oranges';
preg_match_all('/\d+/', $string, $capture);

Полученный массив $capture содержит совпадение на 5 только.

С использованием u Модификатор для включения «UTF-8 mode» и запуска этого:

$string = 'I have ۳ apples and 5 oranges';
preg_match_all('/\d+/u', $string, $capture);

результаты в $capture содержащие совпадения на обоих ۳ а также 5,

Заметки

  • этот вопрос относится к PHP 5.6.22 (самая новая на сегодняшний день)
  • оба теста были выполнены при явном использовании C локали.

3

Решение

Потому что документация не работает. И это, к сожалению, не единственное место, где это так.

PHP использует PCRE под капотом, чтобы реализовать его preg_* функции. Таким образом, документация PCRE является авторитетной. Документация PHP основана на PCRE, но, похоже, вы нашли еще одну ошибку.

Вот что вы можете прочитать в PCRE документы (выделение мое):

По умолчанию символы со значениями больше 128 не соответствуют ни одному из классов символов POSIX. Однако если PCRE_UCP опция передается pcre_compile()некоторые классы изменены так, что используются свойства символов Unicode. Это достигается путем замены определенных классов POSIX другими последовательностями, как показано ниже:

[:alnum:]  becomes  \p{Xan}
[:alpha:]  becomes  \p{L}
[:blank:]  becomes  \h
[:digit:]  becomes  \p{Nd}
[:lower:]  becomes  \p{Ll}
[:space:]  becomes  \p{Xps}
[:upper:]  becomes  \p{Lu}
[:word:]   becomes  \p{Xwd}

Если вы продолжите копаться в документации PHP, вы найдете следующие:

ты (PCRE_UTF8)

Этот модификатор включает дополнительные функции PCRE, несовместимые с Perl. Шаблонные и предметные строки рассматриваются как UTF-8. Этот модификатор доступен из PHP 4.1.0 или выше в Unix и из PHP 4.2.3 в win32. UTF-8 валидность шаблона и предмета проверена начиная с PHP 4.3.5. Недопустимый предмет вызовет preg_* функция ничего не соответствует; неверный паттерн вызовет ошибку уровня E_WARNING, Последовательности UTF-8 с пятью и шестью октетами считаются недействительными начиная с PHP 5.3.4 (соответственно PCRE 7.3 2007-08-28); ранее они считались действительными UTF-8.

Это, к сожалению, ложь. u модификатор в PHP означает PCRE_UTF8 | PCRE_UCP (UCP обозначает Свойства символов Юникода). PCRE_UCP флаг это тот, который меняет смысл \d, \w и тому подобное, как вы можете видеть из документов выше. Ваши тесты подтверждают это.


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

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]