Python正则表达式笔记

import re

0、前言

本篇笔记基于菜鸟教程以及该知乎教程,融入了自己的一些学习心得。

1、正则表达式模式

在这里插入图片描述

高亮处是我的补充,因为根据实际情况确实是能匹配到的
在这里插入图片描述

这边我就偷点懒了哈,直接截的是菜鸟教程的图。

下面这一段大家看的时候要特别仔细🤭,\w有时候是大写\W(非数字字母下划线汉字),有时候是小写\w(数字字母下划线汉字)。

\b\B做进一步的说明,即不仅仅单词是有边界的,任意字符(串),都可以视作有边界,边界位于\w\W发生改变的地方,并且边界是属于前(后)面的字符串的最后一个(第一个)字符,对于单词,比如'Hello world!',在o处,o是属于\w的,后面是空格,属于\W,所以o就是边界;在d处,d是属于\w的,后面是感叹号,属于\W,所以d也是边界。

再比如'What happened ??? Wow!'显然d是happened的边界(d后是空格),我们再来看看对于\W的。第三个?是边界吗?如果你确实这么认为,那么你确实错了,其实第三个问号后的空格才是边界,因为空格是属于\W的,而空格的后面是W,W是属于\W的,所以空格才是边界!

Talk is cheap. Show me the code!

>>>re.findall(r'\w\W\b', '09 8? 7w c_ 9q p@')
['9 ', 'w ', '_ ', 'q ']
>>>re.findall(r'\w\W\b', '09 8? 7w c_ 9q  p@')
['9 ', 'w ', '_ ']

我只在第个命令多输了一个空格,但是结果就不一样了为什么呢?这两个要找的的都是末尾是\w\W的匹配项。我们从q开始看,第一个命令,q属于\w,q后面的空格属于\W,均符合条件,那么空格是不是边界呢,是的!因为空格后面的p是属于\w的,所以'q_'被纳入了列表之中。
再来看第二条命令,此时q后面的第一个空格就不是边界了,因为第一个空格后面还有第二个空格,所以'q_'没有出现在列表里。
\B则正好跟\b相反,可以认为得到补集,不赘述了。

2、正则表达式修饰符 - 可选标志

2.1、re.IGNORECASE(re.I)

虽然第1节是常量,但我们必须先简要提一下re.findall这个函数,因为它是贯穿这一节的函数。
re.findall(pattern, string, flag=0): 从字符串任意位置查找,返回一个列表。pattern是欲匹配的字符(串),string是查找源,flag是修饰符,默认是0

re.I的作用是忽略字符大小写

text = "I'm Jasmine-Feng. My student number is No. 321432"
pattern = r"Jasmine-FENG"
print('Default: ', re.findall(pattern,text))
print('Ignore upper/lower case: ', re.findall(pattern,text,flags=re.I))

N.B. pattern被赋了一个r字符串,这个r字符串的作用是避免转义,r是raw的缩写,也就是保持原样的意思。可看这篇博文。一般来说,使用正则表达式都会用到这个r字符串。

Default:  []
Ignore upper/lower case:  ['Jasmine-Feng']

Process finished with exit code 0

在默认情况下,区分大小写,找不到ENG;若不区分,则可以找到eng。

2.2、re.ASCII(re.A)

re.A的作用是只匹配ASCII码支持的字符,那么具体指哪些字符呢?下图来自百度百科
在这里插入图片描述
汉字是不在这个里面的,所以如果修饰符是re.A的话就匹配不了汉字了哈~

text = "我是Jasmine-Feng. 我的学号是No. 321432"
pattern = r"\w+"
print('Default: ', re.findall(pattern,text))
print('ASCII: ', re.findall(pattern,text,flags=re.A))

\w+的作用是匹配一个或多个字母数字下划线汉字

Default:  ['我是Jasmine', 'Feng', '我的学号是No', '321432']
ASCII:  ['Jasmine', 'Feng', 'No', '321432']

Process finished with exit code 0

2.3、re.DOTALLre.S

在正则表达式模式中,.是用来在这里插入图片描述

text = "我\t是Jasmine-F\neng. 我%的◉学号是No. 321432"
pattern = r'.*'
print('Default: ', re.findall(pattern,text))
print('DOTALL: ', re.findall(pattern,text,re.S))

.*的作用是匹配长度至少为0的字符(串),emmm,好像是句废话?事实上,只要整段话不被换行符截断,就可以得到整个字符串(外加一个空字符串)。

Default:  ['我\t是Jasmine-F', '', 'eng. 我%的◉学号是No. 321432', '']
DOTALL:  ['我\t是Jasmine-F\neng. 我%的◉学号是No. 321432', '']

Process finished with exit code 0

2.4、re.MULTILINE(re.M)

$匹配定位到字符串末尾,^定位到字符串开头,默认情况下,如果换行,是不能定位到新一行的行头/尾的,而用re.M修饰则可以,也就是多行模式。

text = "我\t是Jasmine-F\neng. 我%的◉\n学号是No. 321432"
pattern = r'.$'
pattern2 = r'^.'
print('Default, end: ', re.findall(pattern, text))
print('MULTILINE, end: ', re.findall(pattern, text, re.M))
print('Default, start: ', re.findall(pattern2, text))
print('MULTILINE, start: ', re.findall(pattern2, text, re.M))
Default, end:  ['2']
MULTILINE, end:  ['F', '◉', '2']
Default, start:  ['我']
MULTILINE, start:  ['我', 'e', '学']

Process finished with exit code 0

2.5、re.VERBOSE(re.X)

verbose是“详实的、冗长的”意思,通过该修饰符可以在正则表达式中加入注释。注意,是往pattern里面加,不是往text加!我一开始以为是可以往text加注释,然后调试半天都得不到结果。。。

text = '朋友们好啊!我是xxxxxx拳掌门人xxx~'
pattern = r'''朋友们  # 主语
              好啊!  # 谓语
           '''
print(re.findall(pattern, text,re.VERBOSE))
['朋友们好啊!']

Process finished with exit code 0

2.6、修饰符的叠加

使用|可以叠加修饰。

text = 'Hello everybody!\n我是xxxxxx拳掌门人xxx~'
pattern = r'BODY.*$'
print(re.findall(pattern, text, re.I))
print(re.findall(pattern, text, re.M))
print(re.findall(pattern, text, re.M | re.I))
[]
[]
['body!']

Process finished with exit code 0

3、正则表达式函数

3.1、查找单个匹配项的函数

函数功能
search从任意位置开始搜索
match从开头搜索,不用完全匹配
fullmatch从开头搜索,必须完全匹配

这一类函数返回的都是Match对象,要用group方法以获得匹配值
在这边顺便提一下正则表达式模式中的圆括号(),它的作用是按照常见说法是分组,而根据我的理解,其实就是给他一个标记,方便后面引用,对于findall finditer这样的函数来说,pattern有没有圆括号是无所谓的,而对于search match fullmatch这样返回Match对象的就有所谓了。
group(1)返回第一个组,group(2)返回第二个组,依此类推,当n大于0,group(n)可以看第n个组;而当n为0或者不给group输任何参数的话,则返回的是整个匹配项

Example 3.1.0

text = 'Call me 掌门人,call me 掌门人。Call me 掌门人; call me 掌门人!'
pattern = r'([\w\s]+)([^\w\s])([\s\w]+)'
pattern2 = r'[\w\s]+[^\w\s][\s\w]+'
print(re.findall(pattern,text))
print(re.findall(pattern2,text))
print(re.search(pattern,text).group(0))
print(re.search(pattern,text).group(1,2,3))

pattern中分出了中文标点前、中文标点本身、中文标点后三个部分。

[('Call me 掌门人', ',', 'call me 掌门人'), ('Call me 掌门人', ';', ' call me 掌门人')]
['Call me 掌门人,call me 掌门人', 'Call me 掌门人; call me 掌门人']
Call me 掌门人,call me 掌门人
('Call me 掌门人', ',', 'call me 掌门人')

Process finished with exit code 0

其实后面的部分我本来是写了自己的例子的,但是浏览器给我误关了,又没保存(心态直接炸裂😟),所以3.1和3.2两小节从这边开始,我将使用知乎教程的例子。

Example 3.1.1

在这里插入图片描述

Example 3.1.2

在这里插入图片描述

Example 3.1.3

在这里插入图片描述

3.2、查找多个匹配项的函数

函数功能
findall返回含匹配项的列表
finditer返回含匹配项的迭代器

Example 3.2.1

在这里插入图片描述
从内存角度来看,迭代器占用内存更少。

如果可能存在大量的匹配项的话,建议使用finditer函数,一般情况使用findall函数基本没啥影响。

3.3、分割

使用到split方法,参数:re.split(pattern, string, maxsplit=0, flags=0)
其中maxsplit是最大分割次数,缺省值是0,注意并不是0就不分割了,相反,0是有多少就分割多少,可以理解为一个极限值(正无穷?)。其余参数前文皆已提及。

Example 3.3.1

text = 'Call me 掌门人,call me 掌门人。Call me 掌门人; call me 掌门人!'
pattern = r'[^\s\w]+'
res1 = re.split(pattern,text,maxsplit=0,flags=re.I)
print('split: ', res1)
print(re.findall(pattern,text))
print(re.search(pattern,text))

[^\s\w]+表示除了(数字字母下划线空格)外的任意一个或多个字符

split:  ['Call me 掌门人', 'call me 掌门人', 'Call me 掌门人', ' call me 掌门人', '']
[',', '。', ';', '!']
<re.Match object; span=(11, 12), match=','>

Process finished with exit code 0

3.4、替换

替换要用到sub函数,基本语法是re.sub(pattern, repl, string, count=0, flags=0) ,其中repl是替换的新字符(串)或函数,count是最大替换次数,缺省值是0(同样表示有多少替换多少)。来看个实例。

Example 3.4.1

text = 'Call me 掌门人,call me 掌门人。Call me 掌门人; call me 掌门人!'
pattern = r'([^\w\s])'
repl1 = ','
repl2 = lambda matchobj: ',,' if matchobj.group() in [',',';'] else '..'
print(re.sub(pattern,repl1,text))
print(re.sub(pattern,repl2,text))

repl1中,我们把匹配对象替换成了中文的逗号;而在repl2中,我们把匹配对象替换成了两个英文逗号或两个英文句点。注意matchobj后面跟上了group方法,这是因为直接返回的是Match对象。

Call me 掌门人,call me 掌门人,Call me 掌门人, call me 掌门人,
Call me 掌门人,,call me 掌门人..Call me 掌门人,, call me 掌门人..

Process finished with exit code 0

subn函数与sub函数作用基本相同,只是返回值不同,subn返回的是一个元组,元组是这样构成的:(字符串,替换次数)

Example 3.4.2

text = 'Call me 掌门人,call me 掌门人。Call me 掌门人; call me 掌门人!'
pattern = r'([^\w\s])'
repl1 = ','
repl2 = lambda matchobj: ',,' if matchobj.group() in [',',';'] else '..'
print(re.subn(pattern,repl1,text))
print(re.subn(pattern,repl2,text))
print(re.findall(pattern,text))
('Call me 掌门人,call me 掌门人,Call me 掌门人, call me 掌门人,', 4)
('Call me 掌门人,,call me 掌门人..Call me 掌门人,, call me 掌门人..', 4)
[',', '。', ';', '!']

Process finished with exit code 0

Example 3.4.3

如果 repl 中含有反斜杠 \,则需要使用四个反斜杠来表示:\\\\

text = 'Call me 掌门人,call me 掌门人。Call me 掌门人; call me 掌门人!'
pattern = r'([^\w\s])'
repl = '\\\\'
s = re.sub(pattern, repl, text)
print(s)
'Call me 掌门人\\call me 掌门人\\Call me 掌门人\\ call me 掌门人\\'
f = open('test.txt', 'w')
f.write(s)
f.close()

打开test.txt 查看
在这里插入图片描述

3.5、编译正则对象

re.compile(pattern, flags=0)

compile函数将正则表达式的样式编译成正则表达式对象Pattern。这个对象与re模块有同样的正则函数。

在下一节中我们讲具体讲解Pattern对象。

4、正则对象Pattern

在上节中已提及,Pattern对象可以通过compile函数编译获得,此正则对象拥有与re模块相同的函数。
例如,Example 3.4.2可以写成先预编译成Pattern对象,再调用Pattern对象的subn方法。

text = 'Call me 掌门人,call me 掌门人。Call me 掌门人; call me 掌门人!'
pattern = r'([^\w\s])'
pattern_obj = re.compile(pattern)
repl1 = ','
repl2 = lambda matchobj: ',,' if matchobj.group() in [',',';'] else '..'
print(pattern_obj.subn(repl1,text))
print(pattern_obj.subn(repl2,text))
print(pattern_obj.findall(text))
('Call me 掌门人,call me 掌门人,Call me 掌门人, call me 掌门人,', 4)
('Call me 掌门人,,call me 掌门人..Call me 掌门人,, call me 掌门人..', 4)
[',', '。', ';', '!']

Process finished with exit code 0

其实compile函数 与 其他 re函数(search、split、sub等等) 内部调用的是同一个函数,最终还是调用正则对象的函数!

官方文档推荐:在多次使用某个正则表达式时推荐使用正则对象Pattern 以增加复用性,因为通过 re.compile(pattern) 编译后的模块级函数会被缓存!

感谢菜鸟教程和知乎教程!(链接在文首给出)

完。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值