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

37 投票
3 回答
23117 浏览
提问于 2025-04-16 23:36

我需要在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模块可以解析这种字符串吗?

3 个回答

4

因为你想使用PCRE正则表达式,而Python的re模块已经和最初的PCRE有些不同,所以你可能还想看看Arkadiusz Wahlig为PCRE提供的Python绑定。这样你就可以直接使用原生的PCRE,不用再在不同的正则表达式风格之间转换了。

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 的 正则表达式教练 时发现这个的,感谢那些评论让我想起了它的存在。

81

在Python中使用非ASCII字符时要特别小心

Python在处理非ASCII字符时,有一些非常微妙的问题。有时候,它处理得不好。而且,这些问题不仅和你使用的Python版本有关,还和你是否使用了“宽构建”有关。

一般来说,当你在处理Unicode时,Python 3的宽构建效果最好,而Python 2的窄构建效果最差。不过,不管怎样,所有的组合都和Perl的正则表达式在处理Unicode时有很大差距。如果你想在Python中使用ᴘᴄʀᴇ模式,可能需要寻找比它的旧re模块更远的地方。

令人烦恼的“宽构建”问题已经最终解决了——只要你使用的是足够先进的Python版本。以下是来自v3.3版本说明的一段摘录:

功能

根据PEP 393引入的变化如下:

  • Python现在始终支持完整的Unicode字符范围,包括非BMP字符(即从U+0000到U+10FFFF)。窄构建和宽构建之间的区别不再存在,Python现在在Windows下也表现得像宽构建。
  • 随着窄构建的消亡,特定于窄构建的问题也得到了修复,例如:
    • 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一起使用(参见问题13054)。
  • ./configure标志--with-wide-unicode已被移除。

Python正则表达式的未来

与标准Python发行版中的re库相比,Matthew Barnett的regex模块在几乎所有方面都要好得多,并且很可能最终会取代re。它与你的问题特别相关,因为他的regex库在各方面都比现在的re更兼容Perl,这将使你更容易将Perl的正则表达式移植到Python中。由于这是一次从头开始的重写(不是像汉堡那样的重写 :),它在设计时就考虑到了非ASCII字符,而re并没有。

因此,regex库在处理事情时更符合UTS#18: Unicode正则表达式的(当前)建议。它在大多数方面都满足或超过了UTS#18的一级要求,而这些通常需要使用ICU正则表达式库或Perl本身,或者如果你特别勇敢,可以使用Java 7对其正则表达式的新更新,因为它也符合UTS#18的一级要求

除了满足这些一级要求(这些都是基本的Unicode支持所必需的),但Python当前的re库并不满足,这个强大的regex库还满足RL2.5命名字符(\N{...})、RL2.2扩展字素簇(\X)以及来自UTS#18第14版的新RL2.7关于完整属性的要求。

Matthew的regex模块还支持Unicode的大小写折叠,因此在Unicode上进行不区分大小写的匹配时能可靠工作,re则不支持。

regex现在支持完整的Unicode大小写折叠,像Perl和Ruby一样。

一个非常小的区别是,目前Perl的不区分大小写的模式使用完整的字符串导向大小写折叠,而他的regex模块仍然使用简单的单字符导向大小写折叠,但这是他正在研究的内容。这实际上是一个非常困难的问题,除了Perl,只有Ruby尝试过。

在完整的大小写折叠下,这意味着(例如)"ß"现在正确匹配"SS""ss""ſſ""ſs"(等等),当选择不区分大小写的匹配时。(这在希腊字母中比在拉丁字母中更重要。)

还可以查看我在OSCON2011的第三次演讲的幻灯片或文档源代码,标题为Unicode支持大比拼:好的、坏的和(大多数)丑陋的,讨论JavaScript、PHP、Go、Ruby、Python、Java和Perl中的Unicode支持的普遍问题。如果你不能使用Perl正则表达式或可能的ICU正则表达式库(可惜它没有命名捕获!),那么Matthew的regex可能是你最好的选择。


Nᴏᴛᴀ Bᴇɴᴇ s.ᴠ.ᴘ. (= s’il vous plaît, et même s’il ne vous plaît pas :) 以下的非商业性广告并不是由Python regex库的作者放置的。 :)

酷炫的regex特性

Python的regex库有丰富的超酷特性,其中一些在其他任何正则表达式系统中都找不到。这使得无论你是因为其ᴘᴄʀᴇ兼容性还是出色的Unicode支持,都非常值得一试。

这个模块的一些突出特性包括:

  • 可变宽度的后向查找,这是正则表达式引擎中非常罕见的特性,当你真的需要它时没有它会非常令人沮丧。这可能是正则表达式中请求最多的特性。
  • 反向搜索,这样你就不必先自己反转字符串。
  • 作用域ismx类型选项,例如(?i:foo)只对foo进行大小写折叠,而不是整体,或者(?-i:foo)仅在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{...}命名字符支持,但它在计算上有极大的优势,使其成为在处理正则表达式时,尤其是在网络查询中防止基于饥饿的拒绝服务攻击的首选引擎。它通过禁止回溯引用来实现这一点,回溯引用会使正则表达式不再是常规的,并可能导致时间和空间的超指数爆炸。

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

撰写回答