Python入门系列24 - 正则表达式(二)

共 4416字,需浏览 9分钟

 ·

2019-10-19 09:50

Python入门系列24

ca79373656df2cda12544fe94bf7bee2.webp

正则表达式(二)


本篇阅读时间约为 11分钟。


1

前言


今天继续来介绍一下python的正则表达式,回顾一下上次介绍的re模块整篇文章围绕着re.findall()来进行实例的讲解,也就是所谓的查询操作。为了便于回顾,这里给出链接:python入门系列23 - 正则表达式(一)


2

re模块的sub函数


sub:中文有代替的意思。使用re.sub()可以完成我们对原始字符串的替换操作!

先来看下官方函数的参数解释:


re.sub(pattern, repl, string, count=0, flags=0)

pattern : 正则中的模式字符串。

repl : 替换的字符串,也可为一个函数。

string : 要被查找替换的原始字符串。

count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。

flags : 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。(如:re.I 使匹配对大小写不敏感)


一大波实例来袭,做好准备哟!


实例1(替换普通字符):


假设我数学和英文考试都得了60分……然而我并不甘心,上天给了我一次作弊的机会,可以通过程序来替换分数!如何做呢?


import re
words = 'My math scored 60 points,My English has also scored 60 points.'
# 可以看到我的count参数给出的是0# 默认匹配所有60分,修改为100分
new_words = re.sub('60','100',words,0)
print(new_words)

>>> My math scored 100 points,My English has also scored 100 points.


同理,若是只想修改数学成绩,则将count参数改为1即可:


e1f58e9f2ba48a48d634c5d56cb0743f.webp


实例2(有逻辑的函数替换):


假设我的数学成绩考了85分!emmm相当不错了,但是我依然想让它增加5分,我爱数学!而英语这次考了100分,因为作弊了……为了不让老师察觉,我决定偷偷降低点分数!两种条件,如何才能使用sub函数来完成呢?别忘了我们的repl(replace)参数是可以传递函数的!这也是Python的一个特性,参数可以传递函数,如下:


import re
def modify_score(score):
   print(score)
   new_socre = score.group()    
   if int(new_socre) == 85:        
       return '90'
   if int(new_socre) == 100:        
       return '80'

words = 'My math scored 85 points,My English scored 100 points.That\'s cool!'
new_words = re.sub('\d{2,3}',modify_score,words,0)
print(new_words)


c92266fb86c030416adf8edbc4a658b1.webp


讲解一下:在modify_score中,使用传入score,可以打印输出看下它的类型,这是一种正则的类型,而非字符串类型。此类型要将其内容本身匹配出来,则需要调用它的group()方法,new_score取出来的便是数字形式的字符串。下面的逻辑对比,需要将字符串转换为int型,才可以进行对比操作,否则会报错哟。


综上所述: re.sub()可以轻松的实现字符串的替换,并且可以用函数传递的方式来对原始字符串进行业务逻辑的判断。


3

re模块其它常用函数


1. match

依然是用事例来解释看下:


import re
words = 'My math scored 85 points,My English scored 100 points.That\'s cool!'
new_words = re.match('\d{2,3}',words)
print(new_words)

>>> None


上述结果,打印为None,Why???

因为match函数在进行匹配的时候,是从字符串的起始开始进行匹配,若开头的字符串不符合正则表达式,则直接返回None,也就是什么都没匹配到,停止后续匹配。


所以如果修改words,让我们的分数在字符串开头试下:

68cd995793ffa5bb64d6032e7d36263f.webp


2. search

使用search的示例如下:


import re
words = 'My math scored 85 points,My English scored 100 points.That\'s cool!'
new_words = re.search('\d{2,3}',words)
print(new_words)
print(new_words.group())

>>> <_sre.SRE_Match object; span=(15, 17), match='85'>
>>> 85


上述结果,打印出了匹配到的对象与值,与match不一样!但是可以看到,search函数只匹配到了一次的,后续分数的100即使符合正则表达式,也不会被匹配到了。


match和search函数是当今市场上各教程类中,写爬虫最常用的两个方法,而实际上这两个函数是需要对匹配后的结果进行group分组处理,才能取出你想要的结果。在上节的小课堂中,全篇讲到的findall方法,则不需要进行group的调用,可以直接将结果以list全面匹配出来,这也是findall和match、search函数最大的区别。


4

group分组


既然提到了group分组,那么就来介绍下,group的具体用法,以及在爬虫中经常会以一种什么样的方式去进行你想要的匹配!


此处以search函数来举例,场景是酱紫的:我自己说了一句话 ,「我在这次考试中,数学分数考了85,英语分数考了100分,这真是太酷了!」。现在需要将英语分数考了100分」获取到!代码如下:


import re
words = 'My math scored 85 points,My English scored 100 points.That\'s cool!'

new_words1 = re.search('points,\wThat',words)
new_words2 = re.search('points,\w*That',words)
new_words3 = re.search('points,.*That',words)
new_words4 = re.search(',(.*)That',words)

print(new_words1.group())
print(new_words2.group())
print(new_words3.group())
print(new_words4.group())


仔细看过代码的同学,肯定会疑惑为什么写了这么多行正则匹配的表达式,莫急莫急,且听我慢慢道来。在不看我下面的解释前,你认为,下面这四个print()会依次打印出什么样的结果呢?


解释:其实new_words1~4是一段非常有逻辑的演变过程。

① 先来看new_word1,如果想要匹配我英语分数考了100分这段英文字符,需要先使用普通字符串进行边界的限定,还记得吗?(上节小课堂有说过哦!忘了的话回到文章首处点击链接进行回顾。) 所以使用"ponit, ""That"进行边界限定,一旦边界限定后,便可以使用元字符来进行内容的匹配,而\w代表的含义是匹配包括下划线的任何单词字符。于是调用如下图:


ebd0de30ece904e56b1c956be2f1d182.webp


可以看到报错啦,错误说的是None类型没有group方法,因为1中的正则是错误的,没有正确进行匹配,所以最终得到的值是None。理由很简单,因为\w这样的写法只进行了单字符匹配,故然是不行的。如何进行多字符匹配呢?于是有了new_word2。


② 来看new_word2,普通字符的边界限定写法是不变的,在原来的基础上,在\w后面新增**的元字符代表的含义是匹配前一个字符0次或n次以上。然后有了下图:


32c3552abaa2d063d2760b05a9ff0489.webp


为毛还是报错?然后在反思下,发现。。。哦原来是想匹配的字符串中是包含空格的,而\w是不能匹配到空格字符的,所以这个正则表达式的写法依然不对!再继续,什么元字符可以进行空格和字母的匹配呢?于是有了new_words3。


③ new_words3中使用的是.来进行空格和字母的匹配,点的后面还是使用*来匹配。如下图:


ab83990459f4f059ec35f241bab71182.webp

可以看到正确的结果了,终于没有报错了!但是呢,但是呢,但是呢。。。会发现,匹配到的结果却不是我想要的最终结果,它把整个正则表达式的限定边界也匹配到了...于是再次改进,就有了最后的new_words4。


④ new_words4是在③的基础上,除去了边界字符的限定,将.*小括号扩了起来,此处就是在上节小课堂中提到的分组!而一旦分好组之后,我们便可以通过group将自己定义好的分组提取出来,清理下以上注释掉的代码:


import re
words = 'My math scored 85 points,My English scored 100 points.That\'s cool!'
new_words4 = re.search(',(.*)That',words)
print(new_words4)
print(new_words4.group())
print(new_words4.group(0))
print(new_words4.group(1))


76548049f4f5fca60a6fa0480467d78f.webp


不难发现,其实group()中是可以传递数字的,默认情况下不写等同于0,可以看到上图不写和写0是一个输出结果。这里需要说下,为什么0匹配出来的是整段正则表达式的字符串,而不是我想要的“My English scored 100 points.”这句话。先来看下两段代码:

new_words4 = re.search(',(.*)That',words)
new_words5 = re.search('(,(.*)That)',words)


实际上,new_words4是等效于new_words5的,在new_words5中默认的正则参数位置是有一层隐形的小括号,也就是最外面的组,所以当我们调用group(0)或者group()实则是指这层组的关系。


若想调用组中组,需要从左到右,数字是从1开始进行调用的,和上面的代码结果图的最后一行是对应上的!


5

总结


到这里,关于Python的正则表达式常用的几点也就介绍完了,为什么要讲述正则呢?因为这是处理字符串文本必不可少的一个强大工具!只要能得心应手的使用它,相信不论是在处理爬虫想要的信息也好,数据清洗时处理也罢,都可以轻松玩转字符串!


用思维导图来回顾下正表达式的所有常用知识点吧,所谓一图胜过千言万语,毕竟可以来梳理思路


9b1ca0b9a54d8eb8c7440038f88f82f8.webp


至此完!

浏览 32
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报