涉及星体平面的 unicode 范围的 Python 语义



如果正则表达式中的一个或两个端点在 BMP 之外,正则表达式中字符范围的预期语义究竟是什么?我观察到以下输入在 Python 2.7 和 3.5 中的行为不同:

import re
bool(re.match(u"[u1000-U00021111]", "u1234"))

在我的 2.7 中我得到 False ,在 3.5 中我得到True.后者对我来说是有道理的。前者可能是由于U00021111由一对ud844udd11代孕代表,但即便如此,我也不明白,因为u1000-ud844应该包括u1234就可以了。

  • 这是在某处指定的吗?
  • 这是预期行为吗?
  • 这是否仅取决于 Python 版本,或者还取决于有关 UTF-16 与 UTF-32 的编译时标志?
  • 有没有办法在没有大小写区分的情况下获得一致的行为?
  • 如果区分情况是不可避免的,那么条件到底是什么?

只需使用带有输入字符串的 u 前缀即可告诉 Python 它是一个 Unicode 字符串:

>>> bool(re.match(u"[u1000-U00021111]", u"u1234")) # <= See u"u1234"
True

在 Python 2.7 中,每次处理字符串时都需要将字符串解码为 Unicode。在 Python 3 中,默认情况下所有字符串都是 Unicode,并在文档中进行了说明。

这是我到目前为止发现的。

被 Python 2.2 接受的 PEP 261 引入了一个编译时标志,以使用窄 UTF-16 表示或宽 UTF-32 字符表示来构建 unicode 支持。检查hex(sys.maxunicode)len(u'U00012345')以在运行时区分这些:窄构建将报告最大0xffff2长度,宽构建最多报告0x10ffff和长度1。Python 3.3 的 PEP 393 隐藏了 unicode 字符串的实现细节,使所有字符串看起来都像 UTF-32(除非必要,否则实际上不会浪费那么多空间)。因此,3.3 之前的窄构建会将星体平面上的代码点分解为代理项对,并独立处理各个代理项,以构造正则表达式和要匹配的字符串。或者至少我找不到相反的迹象。

正如 Wiktor 指出的那样,我的例子非常愚蠢,因为我忘记了第二个字符串文字的u前缀。因此,Python 2 不会将其解析为转义序列,而是解析为字节字符串。这就解释了为什么即使在考虑了代理项对之后,代码点似乎也不包含在该范围内。

至于预期的行为:从Python 3.3开始,基于构建类型的区别应该过时了。将每个代码点视为一个单元,无论平面如何,都应该是Python 3的前进方向。但是,对于旧版本来说,窄版本的向后兼容性带来了一个相互矛盾的目标。

最新更新