10

我有一个正则表达式来检测 unicode 字符串中的无效 xml 1.0 字符:

bad_xml_chars = re.compile(u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]', re.U)

在 Linux/python2.7 上,这可以完美运行。在 Windows 上会引发以下问题:

  File "C:\Python27\lib\re.py", line 190, in compile
    return _compile(pattern, flags)
  File "C:\Python27\lib\re.py", line 242, in _compile
    raise error, v # invalid expression
  sre_constants.error: bad character range

任何想法为什么这不在 Windows 上编译?

4

3 回答 3

16

您在 Windows 上构建了一个狭窄的 Python,因此 Unicode 使用UTF-16。这意味着高于 Unicode 字符\uFFFF将是 Python 字符串中的两个单独字符。您应该看到如下内容:

>>> len(u'\U00010000')
2
>>> u'\U00010000'[0]
u'\ud800'
>>> u'\U00010000'[1]
u'\udc00'

以下是正则表达式引擎将如何尝试在窄构建中解释您的字符串:

[^\x09\x0A\x0D\u0020-\ud7ff\ue000-\ufffd\ud800\udc00-\udbff\udfff]

您可以在此处看到\udc00-\udbff无效范围消息的来源。

于 2012-12-13T18:03:56.120 回答
7

它不起作用,因为 Windows 版本的 Python 使用 16 位来表示 unicode 字符,编码为 UTF-16。代码点10000及以上代码点在 UTF-16 中表示为两个代码单元,这混淆了re范围表示,它期望-.

这是您传递给的字符串如何re.compile拆分为字符的方式:

>>> [x for x in u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]']
[u'[', u'^', u'\t', u'\n', u'\r', u' ', u'-', u'\ud7ff', u'\ue000', u'-', 
 u'\ufffd', u'\ud800', u'\udc00', u'-', u'\udbff', u'\udfff', u']']

请注意,\U00010000-\U0010FFFF它表示为 5 个字符:

u'\ud800', u'\udc00', u'-', u'\udbff', u'\udfff'

在字符集中[...],将其re.compile解释为字符u'\ud800'andu'\udfff'和 range u'\udc00' - u'\udbff'。这个范围是无效的,因为它的结束小于它的开始,这会导致错误。

于 2012-12-13T18:03:41.197 回答
1

标准库中有一个部分处理坏字符范围(Lib/sre_compile.py:450):

if code1[0] != LITERAL or code2[0] != LITERAL:
    raise error, "bad character range"
lo = code1[1]
hi = code2[1]
if hi < lo:
    raise error, "bad character range"

当它比较字符范围的lohi文字时\U00010000-\U0010FFFF,它们分别是序数5632056319(当然失败了,因为范围似乎是向后的)。

正如其他人所说,这是因为 Python 将您的 8 字符 Unicode 文字视为两个单独的字符。

于 2012-12-13T18:09:54.803 回答