Unicode、正则表达式和PyPy

7
我编写了一个程序,用于向Python正则表达式添加(有限的)Unicode支持,虽然它在CPython 2.5.2上运行良好,但在PyPy(1.5.0-alpha0 1.8.0,实现Python 2.7.1 2.7.2)上无法工作,两者都在Windows XP上运行(编辑:如评论中所述,@dbaupp可以在Linux上成功运行)。我不知道原因,但我怀疑这与我的使用u"ur"有关。完整源代码在此处,相关部分如下:
# -*- coding:utf-8 -*-
import re

# Regexps to match characters in the BMP according to their Unicode category.
# Extracted from Unicode specification, version 5.0.0, source:
# http://unicode.org/versions/Unicode5.0.0/
unicode_categories = {
    ur'Pi':ur'[\u00ab\u2018\u201b\u201c\u201f\u2039\u2e02\u2e04\u2e09\u2e0c\u2e1c]',
    ur'Sk':ur'[\u005e\u0060\u00a8\u00af\u00b4\u00b8\u02c2-\u02c5\u02d2-\u02df\u02...',
    ur'Sm':ur'[\u002b\u003c-\u003e\u007c\u007e\u00ac\u00b1\u00d7\u00f7\u03f6\u204...',
    ...
    ur'Pf':ur'[\u00bb\u2019\u201d\u203a\u2e03\u2e05\u2e0a\u2e0d\u2e1d]',
    ur'Me':ur'[\u0488\u0489\u06de\u20dd-\u20e0\u20e2-\u20e4]',
    ur'Mc':ur'[\u0903\u093e-\u0940\u0949-\u094c\u0982\u0983\u09be-\u09c0\u09c7\u0...',
}

def hack_regexp(regexp_string):
    for (k,v) in unicode_categories.items():
        regexp_string = regexp_string.replace((ur'\p{%s}' % k),v)
    return regexp_string

def regex(regexp_string,flags=0):
    """Shortcut for re.compile that also translates and add the UNICODE flag

    Example usage:
        >>> from unicode_hack import regex
        >>> result = regex(ur'^\p{Ll}\p{L}*').match(u'áÇñ123')
        >>> print result.group(0)
        áÇñ
        >>> 
    """
    return re.compile(hack_regexp(regexp_string), flags | re.UNICODE)

(在PyPy上,“示例用法”中没有匹配项,因此 result 为 None )
重申一下,程序在CPython上运行良好:Unicode数据似乎正确,替换按预期工作,使用示例通过 doctest 和直接在命令行中键入都可以正常运行。源文件编码也是正确的,并且Python中的 coding 指令似乎被识别。
有什么想法可以解决PyPy破坏我的代码的“不同”?许多事情浮现在我脑海中(无法识别的 coding 头,命令行中不同的编码,对 r 和 u 的不同解释),但就我的测试而言,CPython和PyPy似乎表现相同,因此我对下一步该尝试什么毫无头绪。

1
你使用如此老旧且不稳定的PyPy版本有什么特别的原因吗?(最新稳定版本为1.8。) - huon
1
另外,使用[PyPy 1.8.0 with GCC 4.4.3] on linux2,给出的示例对我来说运行良好。因此,下一步要尝试的是升级您的PyPy。 - huon
@dbaupp 嗯...因为这是我机器上安装的版本?(嘿,当时我安装的时候它还是新的...)现在,说真的,我刚刚升级到了1.8.0版本,但仍然得到相同的结果。既然你已经成功地在Linux上运行了它,也许问题只存在于Windows上。我会进一步调查的。 - mgibsonbr
啊,糟糕。就记录而言,对我来说它一开始就可以工作,我只是下载了文件,运行了你的示例,一切都很好。 - huon
PyPy没有开放的漏洞跟踪系统吗?你试过搜索它吗? - Karl Knechtel
1
@KarlKnechtel好的,那应该就是对的(PyPy中的一个错误)。在问题被缩小到Windows系统之后,我进行了进一步的测试(请参见下面的答案),并注意到在命令行中输入Unicode数据时(未显示:在屏幕上打印时相同),以及当这些数据来自使用codecs正确加载的文件时,存在不一致的行为。 - mgibsonbr
2个回答

7

为什么不直接使用Matthew Barnett的超推荐regexp模块呢?

它可以在Python 3和旧版本的Python 2上工作,是re的替代品,处理了你可能需要的所有Unicode内容,还有更多功能。


当然,我考虑过使用其他正则表达式引擎(例如Ponyguruma),最终可能会采用您的建议,谢谢!但是这里的问题实际上不是关于正则表达式,而是关于PyPy在Windows上的Unicode支持(当然,在我提出问题时,我不知道它是什么,因此正则表达式的问题是可能性之一)。顺便说一句,我刚刚看到错误报告已经得到确认。 - mgibsonbr

6

似乎PyPy存在一些编码问题,无论是读取源文件时(可能是无法识别coding头),还是在命令行输入/输出时。我用以下代码替换了我的示例代码:

>>> from unicode_hack import regex
>>> result = regex(ur'^\p{Ll}\p{L}*').match(u'áÇñ123')
>>> print result.group(0) == u'áÇñ'
True
>>>

它在CPython上继续工作并且在PyPy上失败。然而,用其转义字符 - u'\xe1\xc7\xf1' - 替换“áÇñ”确实奏效了:

>>> from unicode_hack import regex
>>> result = regex(ur'^\p{Ll}\p{L}*').match(u'\xe1\xc7\xf1123')
>>> print result.group(0) == u'\xe1\xc7\xf1'
True
>>>

这在两种情况下都能正常工作。我认为问题仅限于这两种情况(源加载和命令行),因为使用 codecs.open 打开 UTF-8 文件时没有问题。当我尝试在命令行中输入字符串 "áÇñ",或者使用 codecs 加载 "unicode_hack.py" 的源代码时,在 CPython 上得到了相同的结果:

>>> u'áÇñ'
u'\xe1\xc7\xf1'
>>> import codecs
>>> codecs.open('unicode_hack.py','r','utf8').read()[19171:19174]
u'\xe1\xc7\xf1'

但在 PyPy 上的结果不同:

>>>> u'áÇñ'
u'\xa0\u20ac\xa4'
>>>> import codecs
>>>> codecs.open('unicode_hack.py','r','utf8').read()[19171:19174]
u'\xe1\xc7\xf1'

更新:Issue1139已提交至PyPy bug跟踪系统,让我们拭目以待...


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