为Python2和Python3编写Unicode正则表达式

5
我可以在Python2中使用ur'something're.U标志来编译正则表达式模式,例如:

$ python2
Python 2.7.13 (default, Dec 18 2016, 07:03:39) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> pattern = re.compile(ur'(«)', re.U)
>>> s = u'«abc «def«'
>>> re.sub(pattern, r' \1 ', s)
u' \xab abc  \xab def \xab '
>>> print re.sub(pattern, r' \1 ', s)
 « abc  « def « 

在Python3中,我可以避免使用'something'甚至是re.U标志:
$ python3
Python 3.5.2 (default, Oct 11 2016, 04:59:56) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> pattern = re.compile(r'(«)')
>>> s = u'«abc «def«'
>>> print( re.sub(pattern, r' \1 ', s))
 « abc  « def « 

但是目标是编写正则表达式,以支持Python2和Python3。在Python3中执行ur'something'会导致语法错误:

>>> pattern = re.compile(ur'(«)', re.U)
  File "<stdin>", line 1
    pattern = re.compile(ur'(«)', re.U)
                               ^
SyntaxError: invalid syntax

由于这是一个语法错误,在Python3中即使在声明模式之前检查版本也不起作用:

>>> import sys
>>> _pattern = r'(«)' if sys.version_info[0] == 3 else ur'(«)'
  File "<stdin>", line 1
    _pattern = r'(«)' if sys.version_info[0] == 3 else ur'(«)'
                                                             ^
SyntaxError: invalid syntax

如何使用Unicode正则表达式来支持Python2和Python3?


虽然在这种情况下,r''可以轻松地通过删除字面字符串来替换为u''

但是对于某些需要保持清晰的复杂正则表达式,例如:r''是必须的。

re.sub(re.compile(r'([^\.])(\.)([\]\)}>"\'»]*)\s*$', re.U), r'\1 \2\3 ', s)

因此,解决方案应包括文字字符串r''的使用,除非有其他方法可以避免。但请注意,使用字符串字面量、unicode_literals或来自__future__的导入是不被推荐的,因为它会在代码库的其他部分引起大量问题,特别是我正在处理的部分,请参见http://python-future.org/unicode_literals.html 代码库不鼓励使用unicode_literals导入的具体原因是因为其中充满了这些字符,每个字符的更改都将极其繁琐,例如:

也许我漏掉了什么,但对于这种情况,似乎你实际上并不需要一个原始字符串... 换句话说,u'(«)' 应该可以正常工作... - mgilson
啊哈,看到更新的问题了。 - alvas
re.escape能否替代原始字符串的使用?就像re.compile(re.escape(u'([^\.])(\.)([\]\)}>"\'»]*)\s*$'), re.U)这样? - alvas
我认为 re.escape 无法真正帮助你。很遗憾他们不支持 ur 前缀,但我猜他们想限制前缀的数量,毕竟,Python 2 不必永远得到支持。- 为什么不在模块基础上使用未来的 unicode_literals,例如仅针对那些实际包含大量复杂正则表达式的文件?对于其余部分,似乎您将不得不加倍反斜杠... - lenz
@lenz 这是因为我不确定 nltk 的多少部分会被 unicode_literals 打破,特别是在 nltk.tokenize.__init__.py 中。这似乎是一个棘手的问题。 - alvas
1
nltk.tokenize.__init__ 有大约6个字符串字面量(所有正则表达式模式),其中三个已经是Unicode。这样留下了三个字符串需要测试。(unicode_literals 不会影响任何被导入的内容。) - lenz
2个回答

1

你真的需要原始字符串吗?对于你的例子,需要一个unicode字符串,但不需要原始字符串。原始字符串是一种方便,但不是必需品 - 只需双倍使用在原始字符串中使用的任何\并使用普通的unicode即可。

Python 2允许将原始字符串与unicode字符串连接(结果为unicode字符串),因此您可以使用r'([^\.])(\.)([\]\)}>"\'' u'»' r']*)\s*$'
在Python 3中,它们都将是unicode,所以这也可以工作。


0

我曾有同样的问题,最终利用危险的 eval() 函数解决了。我知道这不是很美观,但它使我的代码可以在 Python 2 和 Python 3 中运行。

if sys.version_info.major == 2:
    pattern = eval("re.compile(ur'(\u00ab)', re.U)")
else:
    pattern = re.compile(r'(«)', re.U)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接