Python-正则

1、正则的基本概念

相信许多同学在刚接触到正则时与我一样会有一个“为什么使用正则?”的疑问,我用字符串匹配就可以做到,为什么还要学习正则?
接下来用例子解释一下:

Python-正则

  1. 现在你需要在这个文件内找到以Neo开头的语句。
  2. 现在你需要在这个文件内找到以Neo结尾的语句。
  3. 现在你需要在这个文件内找到以下划线和字母开头的语句。

上面的要求并不是很难,以第一个为例,我们只要几行代码就可以搞定:

path = r'C:\Users\wangx\Desktop\Neo.txt'
with open(path) as file:
	for line in file.readlines():
		if line.startswith('Neo'):
			print(line)

也可以定义一个函数来完成

运行结果:

Neo cool

Neo very cool

Neo very very cool

那么,完成以上三个岂不是要写三块代码或者定义三个函数呢?况且这还只是极少的量,如果要匹配ip地址,匹配网站是否有效的时候,每一次匹配都要单独完成,我们是不是可以写一个规则出来?
这时候,正则表达式出现了。

正则表达式:

  1. 使用单个字符串来描述匹配一系列符合某个句法规则的字符串。
  2. 正则表达式是对字符串操作的一种逻辑公式。
  3. 正则表达式常用来处理文本和数据。

顺便介绍一下正则表达式的处理过程:
依次拿出表达式和文本中的字符比较->如果每一个字符都能匹配->匹配成功,否则失败。

引入一个例子:你想买手机,现在需要做一个简单的爬虫去爬淘宝上各个手机的价格,型号,购买量甚至处理器,如果一个个地做各个场景的函数,试想一下需要多少天。。。这时候,上正则吧。


2、re模块

简介

re模块提供的主要函数是research。它的目的是接受一个正则表达式和一个字符串,并返回发现的第一个匹配。如果完全没有找到匹配,re.search返回None。

可以通过最简单的正则表达式来考虑re.search的实际使用,也就是搜索一个简单的字母数字字符串。(入门)

import re
print(re.search(r'Neo','Is Neo the coolest one?'))

我们来看一下结果:<re.Match object; span=(3, 6), match='Neo'>
在此处,正则表达式解析器的工作十分简单。它在字符串中寻找字符串’Neo’,并返回匹配的对象。

注意: re模块还提供了一个名称为match的函数,该函数看上去类似于search,但它们之间有一个重要的区别:match仅仅搜索从字符串开始的第一个匹配。当你实际想要的是在一个字符串的任何位置找到匹配时,很容易错误地使用re.match。如果你需要这么做,最好总是使用re.search并且使用^锚。(码住后面可能会用到哦)

Tips:相信许多读者看到那个r会有点不懂,在这里解释一下:r代表’raw’,在Python解释器中,会将’'解释为转义字符,在前面加个r就不会这样了。就像最开始地一段代码path = r'C:\Users\wangx\Desktop\Neo.txt',如果不加那个r,是无法运行的哦。(在Linux中文档路径用的是/哦)。

match对象

match对象有多种用于获取关于匹配信息的方法。group方法无可争议是最重要的。它返回一个包含匹配文本的字符串:

import re
match = re.search(r'Neo','Is Neo the coolest one?')
print(match.group())

运行结果:Neo

这里补充一点:

re.search有一个限制条件,它仅仅会返回最近一个以match对象形式的匹配。如果在一个内存内存在多个匹配,re.search只会返回第一个。在需要多个匹配时需要注意这一点!

re模块提供了两个函数,findall和finditer。这两个方法都返回所有非重叠匹配(包括空匹配)。而区别在于:re.findall返回一个列表,re.finditer返回一个生成器。**注意:**它们并没有返回一个match对象,它们仅根据正则表达式的内容以字符串或元组的形式返回匹配自身。

下面是一个findall的例子:

import re
print(re.findall(r'e','OMG,Neo is the coolest,right?Oh god,why Neo so cool!'))

来看一下运行结果:
['e', 'e', 'e', 'e']
它返回了带有四个’e’的列表。


3、基本正则表达式

字符组

通俗一点,通过字符组,你可以使用方括号,在方括号内列出所有想要的字符来表示一个字符组。
例如,考虑一个与Neo和neo匹配的正则表达式:[Nn]eo
看一下下面的正则表达式(正好在这里展示一下上面说过的search方法):

import re
print(re.search(r'[Nn]eo','OMG,Neo is the coolest,right?Oh god,why neo so cool!'))
#search匹配到第一个对象时就停止了哦

结果:<re.Match object; span=(4, 7), match='Neo'>

import re
print(re.findall(r'[Nn]eo','OMG,Neo is the coolest,right?Oh god,why neo so cool!'))

结果:['Neo', 'neo']

**注意:**不仅仅只是可以第一个字母不同:

import re
print(re.findall(r'Ne[Oo]','OMG,Neo is the coolest,right?Oh god,why NeO so cool!'))

结果:['Neo', 'NeO']

二次注意:这种字符组仅匹配一个字符:

import re
print(re.findall(r'N[Ee]o','OMG,NEeo is the coolest,right?'))

结果:[]
也就是说,匹配到N后,接下来匹配E或e,接下来匹配o,但是,字符串中E接下来时e而不是o。

区间

为什么要在正则表达式中使用区间呢?
一些字符组非常大,比如,若匹配数字,使用[0123456789]使代码繁杂,若有大小写字母则。。。
正则表达式在字符组中使用 ‘-’ 来代表区间,因此,匹配任意数字的字符组可以写为:[0-9],匹配大写字母:[A-Z],同时匹配大小写字母:[A-Za-z]
**注意:**当你想匹配连字符时,可以在连字符前面加上一个转义字符,比如:[A-Za-z0-9\-]

取反

通俗的来说取反,当你在字符组中使用了表示取反的符号’’,这意味着它会匹配任何指定字符之外的其他字符。比如’[a-z]'则会匹配除了a-z之外的任何字符。
再比如:‘N[^o]’,它表示,匹配到N后,N接下来的字符可以是除了o之外的所有字符
但是正则表达式无法匹配处于字符串末尾的字符:
比如:

import re
print(re.findall(r'o[^e]','Neo'))

结果:[]

快捷方式

字符 释义
普通字符 数字、字母
转义字符 \s 匹配任何空白字符
\S 匹配任何非空白字符
\d 匹配数字
\D 匹配任何非数字字符
\w 匹配字母或数字或下划线或汉字
\W 匹配任何非字母数字字符
通配符 . 表示除换行符以外的任意字符
[] 表示一个字符域。[0-9] [a-z A-Z]
修饰符(对前面的字符进行修饰) 表示0个或1个前面的字符
* 表示0个或多个前面的字符
.* 表示任意字符(贪婪匹配)
+ 表示一个或多个前面的字符
{n} 表示n个前面的字符
{n,} 表示n或大于n个前面的字符
{n,m} 表示n个到m个前面的字符
其他 ^ 表示以什么开头的行
$ 表示以什么结尾的行
() 表示一个字符集

由一名热心学长向我提供了一个地址,那里相对较全面一点:https://blog.csdn.net/weixin_40907382/article/details/79654372

举个例子:\b被称为词边界字符快捷方式:

import re
print(re.search(r'\bNeo\b','Neo'))

结果:<re.Match object; span=(0, 3), match='Neo'>

再来看一个:

import re
print(re.search(r'\bNeo\b','Neoo'))#这里的Neo变成了Neoo

结果:None

现在对快捷方式有一定的认识了吧
**注意:**还有取反快捷方式哦,就像上面那张不太完整的表里一样。
在这里演示一下\b的取反快捷方式\B:

import re
print(re.search(r'Neo\B','Neo'))

结果:None 惊不惊喜,意不意外?
接着看:

import re
print(re.search(r'Neo\B','Neoo'))

结果:<re.Match object; span=(0, 3), match='Neo'>

原来,\b匹配在单词开始时或结束位置长度为0的字符串,而\B匹配不在单词开始或结束位置长度为0的字符串,其实就是相反的意思,大家不妨多尝试几次,会加深印象。

字符串的开始与结束

注意这一点不要与上面的取反搞混了:表示的是字符串的开始,例如:`r’Neo’,区间取反是写在方括号外面,注意别搞混。
而关于作为字符串开始,还需要注意一点:字符需要正则表达式与字符串开始部分匹配,用代码解释一下:

import re
print(re.search(r'^Neo','who is Neo?'))

结果是:None
再来看一个:


import re
print(re.search(r'^Neo','Neo is cool'))

结果:<re.Match object; span=(0, 3), match='Neo'>

不妨在阅读的同时自己亲自体验打下代码尝试哦。

在字符串中表示结束(与^类似):$
例如:

import re
print(re.search(r'Neo$','who is Neo'))

结果:<re.Match object; span=(7, 10), match='Neo'>
而这样就行不通了(与上面的差不多,举一反三一下):

import re
print(re.search(r'Neo$','Neo is cool'))

结果:None

任意字符

这里主要阐述 ‘.’ 的作用:.字符代表任何单个字符(它仅仅只能出现在方括号字符组以外)

下面写几个使用’.'的正则表达式:

import re
print(re.search(r'N.o','Neo is cool'))

结果:<re.Match object; span=(0, 3), match='Neo'>

import re
print(re.search(r'..o','Neo is cool'))

结果:<re.Match object; span=(0, 3), match='Neo'>

注意:.字符有一个无法匹配到的字符——换行符(\n)

import re
print(re.findall(r'.','Neo\n is cool'))

运行结果:['N', 'e', 'o', ' ', 'i', 's', ' ', 'c', 'o', 'o', 'l'] 结果中是没有换行符的

那要怎么做呢(只列举两种,还有一种传入re.DOTALL或re.S作为re.complie的第二个参数的方法,在后面会说到):

匹配所有字符,可以使用([\s\S])、([\d\D])……等方法

比如:

import re
print(re.findall(r'([\s\S])','Neo\n is cool'))

运行结果:['N', 'e', 'o', '\n', ' ', 'i', 's', ' ', 'c', 'o', 'o', 'l']

通过 (.|\n)* 正则表达式来匹配所有字符

比如:

import re
print(re.findall(r'(.|\n)','Neo\n is cool'))

运行结果:['N', 'e', 'o', '\n', ' ', 'i', 's', ' ', 'c', 'o', 'o', 'l'] 可以看到换行符出现了

可选字符

我们到目前看到的正则表达式,在正则表达式中的字符与被搜索的字符串中的字符保持1:1的关系,但有时,例如单词的拼写方式,color与clour、favor与favour等,要怎么编写正则表达式呢?
这时,可以用’?'字符来表示期望该字符出现一次或零次(在你不知道的那个是否出现的字符后面加上?)。例如: (r'colou?r')
代码如下:

import re
print(re.findall(r'colou?r','U mean color or colour?'))

运行结果:['color', 'colour']

再比如:

import re
print(re.findall(r'favou?r','favor in America and favour in English'))

运行结果:['favor', 'favour']

重复

我们之前所编写的正则表达式仅仅是出现了一次的字符或者字符组或者可选的字符组,那如果我们需要同样的字符或者字符组重复出现呢?
这时我们可以用{N}指定一个标记必须重复给定次数,N与标记应该重复的次数对应。
我们先看一个简单的例子:

import re
print(re.search(r'[\d]{3}-[\d]{3}','123-456 is my phone number'))

运行结果:<re.Match object; span=(0, 7), match='123-456'>

注意这里的’-'仅仅是与字面连字符匹配,并没有什么特殊含义。

重复区间

重复区间的概念,简单引入就是你不确定重复的次数,比如有人的qq号是9位,10位或者11为,这里的语法是: {M,N},M是上界,N是下界。
如果你希望匹配9位数的qq号或者10位数的qq号:[\d]{9,10},这种正则表达式是包含性的,与列表中的不同。

import re
print(re.findall(r'[\d]{9,10}','my qq is 123456789 and his qq is 1234567890'))

结果:['123456789', '1234567890']

这里补充一个叫做“贪心”的东西:在大多数情况下,正则表达式是贪心的,即它会尽可能多的匹配,在上面的例子中,如果有10个数字,它就会匹配10个数字,但有时我们并不想让它贪心,也就是让它尽可能少的返回匹配,只需要添加一个‘?’就可以了:

import re
print(re.search(r'[\d]{3,4}?','my qq is 123456'))

运行结果:<re.Match object; span=(9, 12), match='123'>

开闭区间

在某种情况中,你可能不知道你想要匹配的字符重复了几次,因此可以忽略上界,指定上界为正无穷,例如,{1,}用于指定出现一次或多次,没有上界。

import re
print(re.search(r'[\d]{1,}','my qq is 123456'))

结果:<re.Match object; span=(9, 15), match='123456'>

关于下界,我暂时还没有搞明白,等我懂了会写上的。

速写

有两个速写字符:*和+,用来指定常见的重复情况,用+字符代替{1,}(一个或多个,无上界)
*代替{0,}(0个或多个)
例如,上面的例子可以重新编写为:

import re
print(re.search(r'[\d]+','my qq is 123456'))

运行结果:<re.Match object; span=(9, 15), match='123456'>

如果碰到可以使用这两个字符的情况,最好使用它们,因为它们让正则表达式更易读。

4、分组

当使用分组时,匹配出的内容就表示一个分组,在正则表达式中用“()”来表示分组,需要注意的是,有一个隐含的全局分组(即0),就是整个正则表达式,分完组以后,要想获得某个分组的内容,直接使用group()和groups()函数去直接提取就行。

>>> import re
>>> re.search(r'([\d]{3})-([\d]{4})','my qq is 123-4567')
<re.Match object; span=(9, 17), match='123-4567'>

用group()方法返回完整匹配,若要获得单个匹配部分,只需要给group传递一个你希望的分组的对应参数(注意这里分组是从1开始的):

>>> re.search(r'([\d]{3})-([\d]{4})','my qq is 123-4567').group()
'123-4567'
>>> re.search(r'([\d]{3})-([\d]{4})','my qq is 123-4567').group(2)
'4567'

而groups(),用于返回一个对应每一个单个分组的元组:

>>> re.search(r'([\d]{3})-([\d]{4})','my qq is 123-4567').groups()
('123', '4567')

通过以上方法,将正则表达式像这样分解为子组,可以快速获得匹配,并且获取匹配中的所需数据部分。

零分组

对于group()方法来说,group的目标实际上是从匹配中返回一个单独分组。它接受一个可选参数,也就是你想返回的分组的号码,如果忽略参数,默认值为0。在正则表达式中,分组的计数是基于它们在正则表达式中的位置,从1开始。零分组是特殊的,对应完整的匹配,这也就是为什么分组的计数是从1开始的。如果不调用参数,我们所请求的就是零分组,所以我们会获得完整分组。

命名分组

除了按位置编号的分组外,在python正则表达式中还可以用命名分组。命名分组其实还是位置分组,只不过,使用命名分组可以使用其它两种方式来查找分组。先介绍一下命名分组的语法:在开始的“(”后面立刻添加?P。例如,上面的例子可以重写为:r'(?P<first_three>[\d]{3})-(?P<last_four>[\d]{4})'

我们来试一试:

>>> re.search(r'(?P<first_three>[\d]{3})-(?P<last_four>[\d]{4})','my qq is 123-4567')
<re.Match object; span=(9, 17), match='123-4567'>

接下来我们来看使用命名分组所提供的额外两种查找分组方式:

1、通过分组名来查找

>>> import re
>>> match = re.search(r'(?P<first_three>[\d]{3})-(?P<last_four>[\d]{4})','my qq is 123-4567')
>>> match.group('first_three')
'123'

2、通过groupdict方法返回一个字典而非元组,字典中的键对应名称

>>> match.groupdict()
{'first_three': '123', 'last_four': '4567'}

这里需要注意一点:使用gruopdict时,并不会返回完整匹配,它只会返回子组,而且,如果混合命名分组和非命名分组,非命名分组不会出现在groupdict返回的字典中。

5、断言

这一部分比较抽象,我还没达到讲解的水平,所以这一段来源于网络,请根据情况学习。

正则表达式的先行断言和后行断言一共有4种形式:
(?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion)
(?!pattern) 零宽负向先行断言(zero-width negative lookahead assertion)
(?<=pattern) 零宽正向后行断言(zero-width positive lookbehind assertion)
(?<!pattern) 零宽负向后行断言(zero-width negative lookbehind assertion)
这里面的pattern是一个正则表达式。


如同^代表开头,$代表结尾,\b代表单词边界一样,先行断言和后行断言也有类似的作用,它们只匹配某些位置,在匹配过程中,不占用字符,所以被称为“零宽”。所谓位置,是指字符串中(每行)第一个字符的左边、最后一个字符的右边以及相邻字符的中间(假设文字方向是头左尾右)。
下面分别举例来说明这4种断言的含义。
(?=pattern) 正向先行断言
代表字符串中的一个位置,紧接该位置之后的字符序列能够匹配pattern。
例如对”a regular expression”这个字符串,要想匹配regular中的re,但不能匹配expression中的re,可以用”re(?=gular)”,该表达式限定了re右边的位置,这个位置之后是gular,但并不消耗gular这些字符,将表达式改为”re(?=gular).”,将会匹配reg,元字符.匹配了g,括号这一砣匹配了e和g之间的位置。
(?!pattern) 负向先行断言
代表字符串中的一个位置,紧接该位置之后的字符序列不能匹配pattern。
例如对”regex represents regular expression”这个字符串,要想匹配除regex和regular之外的re,可以用”re(?!g)”,该表达式限定了re右边的位置,这个位置后面不是字符g。负向和正向的区别,就在于该位置之后的字符能否匹配括号中的表达式。
(?<=pattern) 正向后行断言
代表字符串中的一个位置,紧接该位置之前的字符序列能够匹配pattern。
例如对”regex represents regular expression”这个字符串,有4个单词,要想匹配单词内部的re,但不匹配单词开头的re,可以用”(?<=\w)re”,单词内部的re,在re前面应该是一个单词字符。之所以叫后行断言,是因为正则表达式引擎在匹配字符串和表达式时,是从前向后逐个扫描字符串中的字符,并判断是否与表达式符合,当在表达式中遇到该断言时,正则表达式引擎需要往字符串前端检测已扫描过的字符,相对于扫描方向是向后的。
(?<!pattern) 负向后行断言
代表字符串中的一个位置,紧接该位置之前的字符序列不能匹配pattern。
例如对”regex represents regular expression”这个字符串,要想匹配单词开头的re,可以用”(?<!\w)re”。单词开头的re,在本例中,也就是指不在单词内部的re,即re前面不是单词字符。当然也可以用”\bre”来匹配。


对于这4个断言的理解,可以从两个方面入手:
1.关于先行(lookahead)和后行(lookbehind):正则表达式引擎在执行字符串和表达式匹配时,会从头到尾(从前到后)连续扫描字符串中的字符,设想有一个扫描指针指向字符边界处并随匹配过程移动。先行断言,是当扫描指针位于某处时,引擎会尝试匹配指针还未扫过的字符,先于指针到达该字符,故称为先行。后行断言,引擎会尝试匹配指针已扫过的字符,后于指针到达该字符,故称为后行。
2.关于正向(positive)和负向(negative):正向就表示匹配括号中的表达式,负向表示不匹配。


对这4个断言形式的记忆:
1.先行和后行:后行断言(?<=pattern)、(?<!pattern)中,有个小于号,同时也是箭头,对于自左至右的文本方向,这个箭头是指向后的,这也比较符合我们的习惯。把小于号去掉,就是先行断言。
2.正向和负向:不等于(!=)、逻辑非(!)都是用!号来表示,所以有!号的形式表示不匹配、负向;将!号换成=号,就表示匹配、正向。

我们经常用正则表达式来检测一个字符串中包含某个子串,要表示一个字符串中不包含某个字符或某些字符也很容易,用[^…]形式就可以了。要表示一个字符串中不包含某个子串(由字符序列构成)呢?
用[^…]这种形式就不行了,这时就要用到(负向)先行断言或后行断言、或同时使用。
例如判断一句话中包含this,但不包含that。
包含this比较好办,一句话中不包含that,可以认为这句话中每个字符的前面都不是that或每个字符的后面都不是that。正则表达式如下:
^((?<!that).)*this((?<!that).)*$^(.(?!that))*this(.(?!that))*$
对于”this is the case”这句话,两个表达式都能够匹配成功,而”note that this is the case”都匹配失败。
在一般情况下,这两个表达式基本上都能够满足要求了。考虑极端情况,如一句话以that开头、以that结尾、that和this连在一起时,上述表达式就可能不胜任了。
如”note thatthis is the case”或者”this is the case, not that”等。
只要灵活运用这几个断言,就很容易解决:
^(.(?<!that))*this(.(?<!that))*$
^(.(?<!that))*this((?!that).)*$
^((?!that).)*this(.(?<!that))*$
^((?!that).)*this((?!that).)*$
这4个正则表达式测试上述的几句话,结果都能够满足要求。

6、标记

有时我们需要轻微改变正则表达式的行为,python提供了用于修改整个表达式的行为,主要说一说常用的吧。
看了下面的相信你就会对标记有一个比较形象的认识了。

不区分大小写

使用re.IGNORECASE(用re.I也行)标记,就会使正则表达式变为不区分大小写。

>>> import re
>>> re.search(r'neo','Neo neo nEo neO NEO',re.IGNORECASE)
<re.Match object; span=(0, 3), match='Neo'>
>>> re.findall(r'neo','Neo neo nEo neO NEO',re.IGNORECASE)
['Neo', 'neo', 'nEo', 'neO', 'NEO']
>>> re.findall(r'neo','Neo neo nEo neO NEO',re.I)
['Neo', 'neo', 'nEo', 'neO', 'NEO']

点匹配换行符

re.DOTALL(用re.S也行),会使正则表达式匹配换行符,这个在上面讲匹配所有字符时有提到,在任意字符那一块,可以再去看看

>>> re.search(r'.+','neo is \n cool')
<re.Match object; span=(0, 7), match='neo is '>

>>> re.search(r'.+','neo is \n cool',re.S)
<re.Match object; span=(0, 13), match='neo is \n cool'>

>>> re.search(r'.+','neo is \n cool',re.DOTALL)
<re.Match object; span=(0, 13), match='neo is \n cool'>

如果传入了这个参数,正则表达式匹配到换行符时会保持贪心,继续匹配下去。

多行模式

使用re.MULTILINE(用re.M也行)
解释一下多行模式是什么意思,比如说,“Neo likes programming\nI like watching\nHe likes,too”,在这串字符中,找到I开头的怎么办呢?

>>> re.search(r'^I','Neo likes programming\nI like watching\nHe likes,too')
>>> 

这样是无结果的,需要传入re.MULTLINE才可以:

>>> re.search(r'^I','Neo likes programming\nI like watching\nHe likes,too',re.M)
<re.Match object; span=(22, 23), match='I'>
>>> 

详细模式

使用re.VERBOSE(re.X)标记使得正则表达式更容易阅读,当你使用了该标记,它会忽略所有的空白,包括换行符(除了字符组中),同时,当你使用了它,它会将“#”当作注释字符(除了字符组中),越复杂的正则表达式,它会越有用。

>>> re.search(r'(?P<first_three>[\d]{3})-(?P<last_four>[\d]{4})','my qq is 123-4567')
<re.Match object; span=(9, 17), match='123-4567'>

上面的代码等价于下面的:

>>> re.search(r"""(?P<first_three>[\d]{3})  #first_three
	-  #signal
	(?P<last_four>[\d]{4}) #last_four
	""",'my qq is 123-4567' #string
	,re.VERBOSE)
<re.Match object; span=(9, 17), match='123-4567'>

调试模式

使用re.DEBUG(没有别名)标记来输出调试信息(以后可能会有用):

>>> re.search(r'(?P<first_three>[\d]{3})-(?P<last_four>[\d]{4})','my qq is 123-4567',re.DEBUG)
SUBPATTERN 1 0 0
  MAX_REPEAT 3 3
    IN
      CATEGORY CATEGORY_DIGIT
LITERAL 45
SUBPATTERN 2 0 0
  MAX_REPEAT 4 4
    IN
      CATEGORY CATEGORY_DIGIT

 0. INFO 4 0b0 8 8 (to 5)
 5: MARK 0
 7. REPEAT_ONE 9 3 3 (to 17)
11.   IN 4 (to 16)
13.     CATEGORY UNI_DIGIT
15.     FAILURE
16:   SUCCESS
17: MARK 1
19. LITERAL 0x2d ('-')
21. MARK 2
23. REPEAT_ONE 9 4 4 (to 33)
27.   IN 4 (to 32)
29.     CATEGORY UNI_DIGIT
31.     FAILURE
32:   SUCCESS
33: MARK 3
35. SUCCESS
<re.Match object; span=(9, 17), match='123-4567'>

使用多个标记

当你想同时使用多个标记时,正确的语法是 re.xxx | re.xxx
比如:re.DOTALL | re.DEBUG

>>> re.search(r'neo','Neo neo nEo neO NEO',re.IGNORECASE | re.DEBUG)
LITERAL 110
LITERAL 101
LITERAL 111

 0. INFO 4 0b0 3 3 (to 5)
 5: LITERAL_UNI_IGNORE 0x6e ('n')
 7. LITERAL_UNI_IGNORE 0x65 ('e')
 9. LITERAL_UNI_IGNORE 0x6f ('o')
11. SUCCESS
<re.Match object; span=(0, 3), match='Neo'>

7、替换

在python中的替换方法是re.sub。它接受三个参数:正则表达式,用于替换的字符串,被搜索的原始字符串。只有实际匹配被替换,如果并没有匹配,re.sub不执行任何操作。
下面一个例子简单描述一下re.sub :

import re
phone = "2004-959-559  # 这是一个国外电话号码"
 # 删除字符串中的 Python注释 
num = re.sub(r'#.*$', "", phone)
print ("电话号码是: ", num)
 # 删除非数字(-)的字符串 
num = re.sub(r'\D', "", phone)
print ("电话号码是 : ", num)

运行结果是:

电话号码是:  2004-959-559 
电话号码是 :  2004959559

再看一个:
将下面字符串中的 ’dog’ 全部替换成 ’cat’

>>> s='I have a dog , you have a dog , he has a dog'
>>> re.sub( r'dog' , 'cat' , s )
' I have a cat , you have a cat , he has a cat '

如果我们只想替换前面两个,则

>>> re.sub( r'dog' , 'cat' , s , 2 )
' I have a cat , you have a cat , he has a dog '

或者我们想知道发生了多少次替换,则可以使用 subn

>>> re.subn( r'dog' , 'cat' , s )
(' I have a cat , you have a cat , he has a cat ', 3)

简单来说,re.sub可以帮助我们去掉与数据无关的格式或数值。

8、已编译的正则表达式

re模块包含了一个函数:complie,它会返回一个已编译的正则表达式对象,这个对象可以复用
还是用例子来解释吧:

>>> SE = re.compile(r'([\d]{3})-([\d]{4})')
>>> SE.search('my qq is 123-4567')
<re.Match object; span=(9, 17), match='123-4567'>

这样,SE就相当于一个正则表达式,在需要使用的时候调用一下就可以了。
同时,使用re.compile还有一个好处,就是可以在search函数中添加一个位置参数,即搜索字符串的开始与结束位置(pos和endpos)

>>> SE = re.compile(r'[\d]+')
>>> SE.search("I ' m 6 years old ,he is 7 years old and she is 8 years old ", pos=7)
<re.Match object; span=(25, 26), match='7'>

可以自己多试试会理解的更好。


加上寒假在家的一段时间,至此,python正则表达式的编写终于完成了!!!,当然这只是些理论的入门必备,后面我还会添加一些实践的项目来练习,感谢阅读,编写不易,如有错误请及时联系!

上一篇:laravel7 手机号验证码登陆


下一篇:自学实前后端践项目2 phone Store 3