Python中的Perl兼容正则表达式(PCRE)

35

我需要在Python中使用PCRE解析一些字符串,但我不知道怎么做。

我想要解析的字符串看起来像这样:

match mysql m/^.\0\0\0\n(4\.[-.\w]+)\0...\0/s p/MySQL/ i/$1/
在这个例子中,我需要获取这些不同的项:
"m/^.\0\0\0\n(4\.[-.\w]+)\0...\0/s" ; "p/MySQL/" ; "i/$1/"

我在Python中找到与PCRE相关的唯一信息是这个模块:http://pydoc.org/2.2.3/pcre.html(但它被写成一个.so文件...)

您知道是否存在某个Python模块来解析这种类型的字符串吗?


只是为了明确:您想要“匹配”PCRE,而不是“使用”它们? - Tim Pietzcker
1
好的,换句话说,您需要有人或某种工具将PCRE正则表达式转换为Python正则表达式?可以看看RegexBuddy - Tim Pietzcker
我正在寻找免费的东西 :) 是的,我正在寻找能够在Python中翻译PCRE的工具。 - Steeve
1
Python在处理正则表达式模式和字符串中的非ASCII字符时存在一些微妙的问题,这些问题根据你使用的Python版本以及是否有“宽构建”而有很大的差异。带有宽构建的Python3效果最佳,而带有窄构建的Python2效果最差,但所有版本都与Perl正则表达式在Unicode方面相差甚远。Matthew Barnett的regex模块适用于Python 2和3,并且比re模块好得多,很可能会最终取代它。另请参见我的第三个OSCON演讲。 - tchrist
3个回答

80

在Python中特别小心非ASCII字符

Python在处理模式和字符串中的非ASCII字符时存在一些微妙的问题,或者说无法处理。更糟糕的是,这些差异不仅取决于您使用的Python版本,而且还取决于您是否有“宽版”。

通常,在处理Unicode时,带有宽版的Python 3效果最佳,而带有窄版的Python 2效果最差,但所有组合与Perl正则表达式相比仍然相差甚远。如果您正在寻找Python中的ᴘᴄʀᴇ模式,则可能需要比旧的re模块更进一步。

令人困扰的“宽版”问题已经最终得到解决——只要您使用足够先进的Python版本。以下是v3.3发布说明的摘录:

功能

PEP 393引入的更改如下:

  • Python现在始终支持完整的Unicode码点范围,包括非BMP码点(即从U+0000到U+10FFFF)。窄版和宽版之间的区别不再存在,即使在Windows下,Python现在也像宽版一样运行。
  • 随着窄版的死亡,窄版特有的问题也已经解决了,例如:
    • len()现在总是返回非BMP字符的1,因此len('\U0010FFFF') == 1
    • 代理对在字符串文字中不再重新组合,因此'\uDBFF\uDFFF' != '\U0010FFFF'
    • 索引或切片非BMP字符返回预期值,因此'\U0010FFFF'[0]现在返回'\U0010FFFF'而不是'\uDBFF'
    • 标准库中的所有其他函数现在都正确处理非BMP码点。
  • sys.maxunicode的值现在始终为1114111(十六进制为0x10FFFF)。PyUnicode_GetMax()函数仍然返回0xFFFF或0x10FFFF以保持向后兼容性,并且不应与新的Unicode API一起使用(请参见issue 13054)。
  • ./configure标志--with-wide-unicode已被删除。

Python正则表达式的未来

与标准Python分发的re库相比,Matthew Barnett的regex模块适用于Python 2和Python 3,并且在所有可能的方面都要好得多,很可能最终取代re。它与您的问题特别相关的是,他的regex库比现在的re更加ᴘᴄʀᴇ(即更加兼容Perl),这将使您更容易地将Perl正则表达式移植到Python中。由于它是从头开始编写的(不是汉堡包那种意思),因此它考虑了非ASCII字符,而re没有。

regex库因此在如何处理事情方面更加密切地遵循UTS#18:Unicode正则表达式(当前)建议。它在大多数情况下符合或超过UTS#18 Level 1的要求,这是您通常必须使用ICU正则表达式库或Perl本身才能满足的要求 - 或者如果您特别勇敢,则使用新的Java 7更新其正则表达式,因为它也符合UTS#18的Level One requirements

除了那些对于基本Unicode支持非常重要的一级要求之外,Python当前的re库无法满足这些要求。而强大的regex库还满足RL2.5命名字符(\N{...}),RL2.2扩展字形群(\X)和UTS#18修订版14全属性的二级要求。
Matthew的regex模块还进行了Unicode大小写转换,因此可以可靠地在Unicode上进行不区分大小写的匹配,而re则不行。
下面的内容已经过时,因为regex现在支持完整的Unicode大小写转换,就像Perl和Ruby一样。
在全案例折叠下,这意味着(例如)当选择不区分大小写匹配时,“ß”现在可以正确匹配“SS”、“ss”、“ſſ”、“ſs”等。 (这在希腊文中比在拉丁文中更为重要。) 另请参见我在第三个OSCON2011演讲的幻灯片或文档源代码,题为“Unicode Support Shootout: The Good, the Bad, and the (mostly) Ugly”,了解跨JavaScript、PHP、Go、Ruby、Python、Java和Perl的Unicode支持的一般问题。如果不能使用Perl正则表达式或可能没有命名捕获的ICU正则表达式库(遗憾!),那么Matthew的Python正则表达式可能是您最好的选择。

Nᴏᴛᴀ Bᴇɴᴇ s.ᴠ.ᴘ. (= please, even if you don't want to :) 下面的非请求性、非商业性广告实际上并不是由 Python 的 regex 库的作者放在这里的。:)

Cool regex 特性

Python 的 regex 库有许多超酷的特性,其中一些在任何其他正则表达式系统中都找不到。这使得它非常值得一试,无论您是使用它的 PCRE 功能还是其出色的 Unicode 支持。

此模块的一些杰出特性包括:

  • 可变宽度回溯,这是一种在正则表达式引擎中非常罕见的功能,当你真正需要它时,没有它会非常令人沮丧。这可能是正则表达式中最常被请求的功能。
  • 向后搜索,这样您就不必自己先反转字符串。
  • 作用域ismx类型选项,使得(?i:foo)仅对foo进行大小写折叠,而不是整个内容,或者通过(?-i:foo)关闭大小写折叠。这是Perl的工作方式(或者说可以这样)。
  • 基于编辑距离的模糊匹配(Udi Manber的agrepglimpse也有此功能)
  • 隐式按最短到最长排序的命名列表,使用\L<list>插值
  • 元字符,只匹配单词的开始或结尾而不是两侧(\m\M
  • 支持所有Unicode行分隔符(Java可以做到这一点,Perl也可以,尽管有些勉强,使用\R参见RL1.6)。
  • 完整的集合操作——并集、交集、差集和对称差集——在括号字符类上RL1.3,这比在Perl中更容易。
  • 允许重复捕获组,例如(\w+\s+)+,其中您可以获取第一个组的所有单独匹配,而不仅仅是最后一次匹配。(我认为C#也可以做到这一点。)
  • 比通过前瞻中的诡计捕获组更直接地获取重叠匹配的方法。
  • 所有组的起始和结束位置,以便进行稍后的切片/子字符串操作,就像Perl的@+@-数组一样。
  • 分支重置运算符通过(?|...|...|...|)在每个分支中重置组编号的方式,与Perl中的工作方式相同。
  • 可以配置在早晨等待您的咖啡。
  • 支持RL2.3中更复杂的单词边界。
  • 默认情况下假定Unicode字符串,并完全支持RL1.2a,使得\w\b\s等在Unicode上工作。
  • 支持\X表示连字号。
  • 支持\G连续点断言。
  • 对于64位构建可以正常工作(re只有32位索引)。
  • 支持多线程。

好的,够炒作了。 :)

又一个优秀的正则表达式引擎

如果你是一个正则表达式爱好者,那么值得一看的最后一个选择是 Python库绑定 到 Russ Cox 的出色的 RE2库。它也支持本地的Unicode,包括简单的基于字符的大小写折叠,并且与 re 不同,它显著提供了Unicode通用类别和Unicode脚本字符属性,这是您在处理较简单的Unicode时最常需要的两个关键属性。

尽管 RE2错过了一些Unicode功能,例如在ICU、Perl和Python中发现的\N{...}命名字符支持,但它具有极其严重的计算优势,使它成为每当您担心在Web查询等中通过regexes引起饥饿型拒绝服务攻击时的选择正则表达式引擎。它通过禁止反向引用来实现这一点,这会导致正则表达式停止变得规则并在时间和空间上产生超指数级的爆炸风险。

RE2的库绑定不仅适用于C/C++和Python,还适用于Perl,特别是Go,在那里它即将很快取代标准的正则表达式库。


4

您正在寻找'(\w/[^/]+/\w*)'

用法如下:

import re
x = re.compile('(\w/[^/]+/\w*)')
s = 'match mysql m/^.\0\0\0\n(4\.[-.\w]+)\0...\0/s p/MySQL/ i/$1/'
y = x.findall(s)
# y = ['m/^.\x00\x00\x00\n(4\\.[-.\\w]+)\x00...\x00/s', 'p/MySQL/', 'i/$1/']

在使用Edi Weitz的Regex Coach时发现了它,因此感谢对问题的评论,让我记起了它的存在。


4

既然您想使用PCRE正则表达式,而Python的re模块已经偏离了其最初的PCRE起源,您可能还想查看Arkadiusz Wahlig的Python PCRE绑定。这样,您将可以访问原生的PCRE,而无需在不同的正则表达式之间进行转换。


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