Python的regex模块——更强大的正则表达式引擎

Python自带了正则表达式引擎(内置的re模块),但是不支持一些高级特性,比如下面这几个:

  • 固化分组    Atomic grouping
  • 占有优先量词    Possessive quantifiers
  • 可变长度的逆序环视    Variable-length lookbehind
  • 递归匹配    Recursive patterns
  • (起始/继续)位置锚\G    Search anchor

幸好,在2009年,Matthew Barnett写了一个更强大正则表达式引擎——regex模块这是一个Python的第三方模块。

除了上面这几个高级特性,还有很多有趣、有用的东西,本文大致介绍一下,很多内容取自regex的文档。

 

无论是编程还是文本处理,regex模块都是一件利器。

用一个指标可以大致了解它的复杂性,re模块有4270行C语言代码,而regex模块有24513行C语言代码。

 

这个模块以后可能被收进Python标准库。目前(2015年)它还在不断发展,经常发布bug修正版,不过感觉用在一般生产环境应该没什么问题。

 

目录

一、安装regex

二、一些有趣的特性

三、模糊匹配

四、两种工作模式

五、Version 0模式和re模块不兼容之处

 

一、安装regex

regex支持Python 2.5+和Python 3.1+,可以用pip命令安装:

pip install regex

PyPy 2.6+也可以使用这个模块。

 

regex基本兼容re模块,现有的程序可以很容易切换到regex模块:

import regex as re

 

二、一些有趣的特性

完整的Unicode支持

1,支持最新的Unicode标准,这一点经常比Python本身还及时。

2,支持Unicode代码属性,包括scripts和blocks。

      如:\p{Cyrillic}表示西里尔字符(scripts),\p{InCyrillic}表示西里尔区块(blocks)。

3,支持完整的Unicode字符大小写匹配,详见此文

      如:ss可匹配ßcliff(这里的ff是一个字符)可匹配CLIFF(FF是两个字符)。

      不需要的话可以关闭此特性。不支持Unicode组合字符与单一字符的大小写匹配,所以感觉这个特性不太实用。

4,regex.WORD标志开启后:

      作用1:\b、\B采用Unicode的分界规则,详见此文

                   如:开启后\b.+?\b可搜索到3.4;关闭后小数点.成为分界符,于是只能搜到['3', '.', '4']

      作用2:采用Unicode的换行符。除了传统的\r、\n,Unicode还有一些换行符,开启后作用于.MULTILINE和.DOTALL模式。

5,\X匹配Unicode的单个字形(grapheme)。

      Unicode有时用多个字符组合在一起表示一个字形,详见此文

      \X匹配一个字形,如:^\X$可以匹配'\u0041\u0308'

 

单词起始位置、单词结束位置

\b是单词分界位置,但不能区分是起始还是结束位置。

regex用\m表示单词起始位置,用\M表示单词结束位置

 

(?|...|...)
重置分支匹配中的捕获组编号。

>>> regex.match(r"(?|(first)|(second))", "first").groups()
('first',)
>>> regex.match(r"(?|(first)|(second))", "second").groups()
('second',)

两次匹配都是把捕获到的内容放到编号为1捕获组中,在某些情况很方便。

 

(?flags-flags:...)

局部范围的flag控制。在re模块,flag只能作用于整个表达式,现在可以作用于局部范围了:

>>> regex.search(r"<B>(?i:good)</B>", "<B>GOOD</B>")
<regex.Match object; span=(0, 11), match='<B>GOOD</B>'>

在这个例子里,忽略大小写模式只作用于标签之间的单词。

(?i:)是打开忽略大小写,(?-i:)则是关闭忽略大小写。

如果有多个flag挨着写既可,如(?is-f:),减号左边的是打开,减号右边的是关闭。

 

除了局部范围的flag,还有全局范围的flag控制,如 (?si-f)<B>good</B>

re模块也支持这个,可以参见Python文档。

把flags写进表达式、而不是以函数参数的方式声明,方便直观且不易出错。

 

(?(DEFINE)...)

定义可重复使用的子句

>>> regex.search(r'(?(DEFINE)(?P<quant>\d+)(?P<item>\w+))(?&quant) (?&item)', '5 elephants') <regex.Match object; span=(0, 11), match='5 elephants'>

此例中,定义之后,(?&quant)表示\d+(?&item)表示\w+。如果子句很复杂,能省不少事。

 

partial matches

部分匹配。可用于验证用户输入,当输入不合法字符时,立刻给出提示。

 

可以pickle编译后的正则表达式对象

如果正则表达式很复杂或数量很多,编译需要较长时间。

这时可以把编译好的正则式用pickle存储到文件里,下次使用直接pickle.load()就不必再次编译了。

 

除了以上这些,还有很多新特性(匹配控制、便利方法等等),这里就不介绍了,请自行查阅文档。

 

三、模糊匹配

regex有模糊匹配(fuzzy matching)功能,能针对字符进行模糊匹配,提供了3种模糊匹配:

  • i,模糊插入
  • d,模糊删除
  • s,模糊替换

以及e,包括以上三种模糊

 

举个例子:

>>> regex.findall('(?:hello){s<=2}', 'hallo')
['hallo']

(?:hello){s<=2}的意思是:匹配hello,其中最多容许有两个字符的错误。

于是可以成功匹配hallo。

 

这里只简单介绍一下模糊匹配,详情还是参见文档吧。

 

四、两种工作模式

regex有Version 0Version 1两个工作模式,其中的Version 0基本兼容现有的re模块,以下是区别:

 Version 0  (基本兼容re模块)Version 1
启用方法

设置.VERSION0或.V0标志,或者在表达式里写上(?V0)。

设置.VERSION1或.V1标志,或者在表达式里写上(?V1)。

零宽匹配

像re模块那样处理:

.split 不能在零宽匹配处切割字符串。

.sub 在匹配零宽之后向前传动一个位置。

PerlPCRE那样处理

.split 可以在零宽匹配处切割字符串。

.sub 采用正确的行为。

内联flag内联flag只能作用于整个表达式,不可关闭。内联flag可以作用于局部表达式,可以关闭。
字符组只支持简单的字符组。字符组里可以有嵌套的集合,也可以做集合运算(并集、交集、差集、对称差集)。
大小写匹配

默认支持普通的Unicode字符大小写,如Й可匹配й

这与Python3里re模块的默认行为相同。

默认支持完整的Unicode字符大小写,如ss可匹配ß

可惜不支持Unicode组合字符与单一字符的大小写匹配,所以感觉这个特性不太实用。可以在表达式里写上(?-f)关闭此特性。

如果什么设置都不做,会采用regex.DEFAULT_VERSION指定的模式。在目前,regex.DEFAULT_VERSION的默认值是regex.V0。

如果想默认使用V1模式,这样就可以了:

import regex
regex.DEFAULT_VERSION = regex.V1

V1模式默认开启.FULLCASE(完整的忽略大小写匹配)。通常用不上这个,所以在忽略大小写匹配时用(?-f)关闭.FULLCASE即可,这样速度更快一点,例如:(?i-f)tag

 

其中零宽匹配的替换操作差异比如明显。绝大多数正则引擎采用的是Perl流派的作法,于是Version 1也朝着Perl的方向改过去了。

>>> # Version 0 behaviour (like re)
>>> regex.sub('(?V0).*', 'x', 'test')
'x'
>>> regex.sub('(?V0).*?', '|', 'test')
'|t|e|s|t|'

>>> # Version 1 behaviour (like Perl)
>>> regex.sub('(?V1).*', 'x', 'test')
'xx'
>>> regex.sub('(?V1).*?', '|', 'test')
'|||||||||'

re模块对零宽匹配的实现可能是有误的(见issue1647489);

而V0零宽匹配的搜索和替换会出现不一致的行为(搜索采用V1的方式,替换采用re模块的方式);

在Python 3.7+环境下,re和regex模块的行为同时做了改变,据称是采用了“正确”的方式处理零宽匹配:总是返回第一个匹配(字符串或零宽),但是如果是零宽并且之前匹配的还是零宽则忽略这和3.6-的re模块、regex的V0模式、V1模式都略有不同。

 

说着挺吓人的,在实际使用中3.6- re、3.7+ re、V0、V1之间极少出现不兼容的现象。

 

五、Version 0模式和re模块不兼容之处

上面说了“Version 0基本兼容re模块”,说说不兼容的地方:

 

1、对零宽匹配的处理。

regex修复了re模块的搜索bug(见issue1647489),但是也带来了不兼容的问题。

 

在re中,用".*?"搜索"test"返回:['', '', '', '', ''],也就是:最前、字母之间的3个位置、最后,总共5个位置。

在regex中,则返回:['', 't', '', 'e', '', 's', '', 't', '']

在实际使用中,这个问题几乎不会造成不兼容的情况,所以基本可以忽略此差异。

 

2、\s的范围。

在re中,\s在这一带的范围是0x09 ~ 0x0D,0x1C ~ 0x1E。

在regex中,\s采用的是Unicode 6.3+标准的\p{Whitespace},在这一带的范围有所缩小,只有:0x09 ~ 0x0D。

十六进制十进制英文说明中文说明
0x099HT (horizontal tab)水平制表符
0x0A10LF (NL line feed, new line)换行键
0x0B11VT (vertical tab)垂直制表符
0x0C12FF (NP form feed, new page)换页键
0x0D13CR (carriage return)回车键
 ... ... ... ...
0x1C28FS (file separator)文件分割符
0x1D29GS (group separator)分组符
0x1E30RS (record separator)记录分离符

 

除此之外,可能还有未知的不兼容之处。

转载于:https://www.cnblogs.com/animalize/p/4949219.html

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Python 中配平化学方程式,首先需要解析出化学方程式中的原子、元素和分子式。这可以通过正则表达式来实现。 然后,需要构建一个矩阵来表示化学方程式,其中的行表示各种不同的元素,列表示反应物和生成物。每个单元格表示每种元素在该物质中的原子个数。 最后,可以使用高斯消元法或者高斯-约旦消元法来求解这个方程组,从而得到各种物质的化学计量数。 下面是一个简单的示例,假设要配平的化学方程式为: 2 H2 + O2 -> 2 H2O 首先,我们可以使用正则表达式来解析出化学方程式中的元素和分子式: ``` import re equation = "2 H2 + O2 -> 2 H2O" # 匹配元素和分子式 elements_regex = r"([A-Z][a-z]*)" molecules_regex = r"(\d*\.?\d*)\s*([A-Z][a-z]*\d*)" elements = re.findall(elements_regex, equation) molecules = re.findall(molecules_regex, equation) print(elements) # ['H', 'O'] print(molecules) # [('2', 'H2'), ('1', 'O2'), ('2', 'H2O')] ``` 接下来,我们可以构建一个矩阵来表示化学方程式,并使用高斯消元法来求解: ``` # 建立 ### 回答2: 配平化学方程式是指将化学方程式中的反应物和生成物的个数和系数调整到最小整数比例的过程。不使用第三方库,可以通过编写代码来实现配平化学方程式的功能。 首先,我们需要输入化学方程式的反应物和生成物。假设输入的化学方程式为"A + B -> C + D"。 然后,我们可以使用一个字典来存储化学方程式中的各个元素及其个数。通过遍历方程式中的每个反应物和生成物,将元素及其个数存储在字典中。 接下来,我们可以设置一个变量来表示每个元素需要的系数,初始值为1。我们需要通过调整这个系数来配平方程式。 然后,我们可以编写一个循环来不断尝试调整系数的值,直到达到配平方程式的目标。在循环中,我们可以先根据目前的系数,计算反应物和生成物中各个元素的总个数差异。 接下来,我们可以遍历字典中的元素及其个数,根据总个数差异和当前的系数,计算添加或减少该元素的个数。然后,我们可以新字典中该元素的个数。 在完成一次循环后,我们可以检查方程式是否配平。如果方程式已经配平,我们可以结束循环。如果方程式还没有配平,我们可以继续调整系数的值,进行下一次循环。 最后,我们可以输出配平后的化学方程式。 以上是用Python不使用第三方库配平化学方程式的一个简单思路。具体代码实现可以根据实际情况和需求进行调整和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值