Семантика Python для диапазонов юникода с участием астральных плоскостей
Какова точная семантика для диапазонов символов в регулярных выражениях, если одна или обе конечные точки диапазона находятся за пределами BMP? Я заметил, что следующий ввод ведет себя по-разному в Python 2.7 и 3.5:
import re
bool(re.match(u"[\u1000-\U00021111]", "\u1234"))
В моем 2.7 я получаю False
, в 3,5 я получаю True
, Последнее имеет смысл для меня. Первое возможно из-за \U00021111
будучи представленным суррогатной парой \ud844\udd11
но даже тогда я не понимаю \u1000-\ud844
должны включать \u1234
просто хорошо.
- Это где-то указано?
- Это намеренное поведение?
- Это зависит только от версии Python или также от флагов времени компиляции относительно UTF-16 против UTF-32?
- Есть ли способ получить последовательное поведение без учета регистра?
- Если различия между делами неизбежны, каковы условия?
2 ответа
Просто используйте u
префикс входной строки, чтобы сообщить Python, что это строка Unicode:
>>> bool(re.match(u"[\u1000-\U00021111]", u"\u1234")) # <= See u"\u1234"
True
В Python 2.7 вам нужно декодировать строки в Unicode каждый раз, когда вы их обрабатываете. В Python 3 все строки по умолчанию являются Unicode, и это указано в документации.
Вот что я узнал до сих пор.
PEP 261, который был принят для Python 2.2, ввел флаг времени компиляции для построения поддержки юникода, используя либо узкое представление UTF-16, либо широкое представление символов UTF-32. Проверьте hex(sys.maxunicode)
или же len(u'\U00012345')
чтобы различать их во время выполнения: узкие сборки сообщат максимум 0xffff
и длина 2
Широкий строит максимум 0x10ffff
и длина 1
, PEP 393 для Python 3.3 скрывает детали реализации строки в юникоде, благодаря чему все строки выглядят как UTF-32 (без необходимости тратить так много места, если это не требуется). Столь узкие сборки до 3.3 будут разлагать кодовые точки на астральных планах на суррогатные пары и обрабатывать отдельные суррогаты независимо как для построения регулярного выражения, так и для строки, с которой нужно сравнивать. Или, по крайней мере, я не нашел указаний на обратное.
Как отметил Виктор, мой пример был довольно глупым, так как я забыл u
префикс второго строкового литерала. Поэтому Python 2 будет анализировать это не как escape-последовательность, а как байтовую строку. Это объясняет, почему это выглядело так, как будто кодовая точка не была включена в этот диапазон даже после того, как были приняты во внимание суррогатные пары.
Что касается предполагаемого поведения: Начиная с Python 3.3 различие, основанное на типе сборки, должно устареть. Рассматривая каждую кодовую точку как единое целое, независимо от плоскости, должен быть путь вперед для Python 3. Но обратная совместимость на узких сборках ставит противоречивую цель для более старых версий.