1 什么是正则表达式
- 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符,及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑。
- 非Python独有,re模块实现
- 在线正则表达式测试
右侧是常用的正则表达式,可以从待匹配的文本中匹配到相应的结果 - 常用的匹配规则
模式 | 描述 |
---|---|
\w | 匹配字母,数字及下划线 |
\W | 匹配不是字母,数字及下划线的字符 |
\s | 匹配任意空白字符,等价于[\t\n\r\f] |
\S | 匹配任意非空字符 |
\d | 匹配任意数字,等价于[0,9] |
\D | 匹配任意非数字的字符 |
\A | 匹配字符串开头 |
\Z | 匹配字符串结尾,如果存在换行,只匹配到换行前的结束字符串 |
\z | 匹配字符串结尾,如果存在换行,同时还会匹配换行符 |
\G | 匹配最后匹配完成的位置 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
^ | 匹配一行字符串的开头 |
$ | 匹配一行字符串的结尾 |
. | 匹配任务字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符 |
[...] | 用来表示一组字符,单独列出,比如[amk匹配a,m,或k] |
[^...] | 不在[]中的字符,比如[^abc]匹配除了a,b,c之外的字符 |
* | 匹配0个或多个表达式 |
+ | 匹配1个或多个表达式 |
? | 匹配0个或1个前面的正则表达式定义的片段,非贪婪方式 |
{n} | 精准匹配n个前面的表达式 |
{n,m} | 匹配n到m次由前面正则表达式定义的片段,非贪婪方式 |
a I b | 匹配a或b |
() | 匹配括号内的表达式,也表示一个组 |
2 re.match
re.match尝试从string 字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就会返回none。
最常规的匹配
re.match(pattern,string,flags=0)
函数有三个参数,第一个就是所写的正则表达式,第二个是所要匹配的目标字符串,第三个是一个匹配模式。
import re
content = 'Hello 123 4567 World_This is s Regex Demo'
print(len(content)) # 要匹配的内容的长度
result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}.*Demo$', content)
# ^、$分别代表匹配一行字符串的开头和结尾
# \s:任意空白字符,
# \d:任意数字,
# {n}:匹配n个前面(此处为数字)的表达式
# \w匹配字母、数字或下划线
# .(点号)匹配任意字符
print(result)
print(result.group()) # 返回匹配结果
print(result.span()) # 匹配结果的范围

泛匹配
import re
content = 'Hello 123 4567 World_This is s Regex Demo'
result = re.match('^Hello.*Demo$', content)
# .*可以匹配中间的所有字符
print(result)
print(result.group()) # 返回匹配结果
print(result.span()) # 匹配结果的范围

匹配目标
import re
content = 'Hello 123 4567 World_This is s Regex Demo'
result = re.match('^Hello\s(\d+)\s.*Demo$', content)
# 把需要匹配的目标用()括起来,然后指定左右端点,此处是两个空格\s
print(result)
print(result.group(1)) # 返回匹配结果,也就是第一个()其中的内容:123
print(result.span()) # 匹配结果的范围

注:group中0和1的区别,在于()的使用。
贪婪匹配
".*"的匹配方式就是竟可能匹配更多的字符(把最后一个数字之前的所有数字都匹配进去了)
import re
content = 'Hello 1234567 World_This is s Regex Demo'
result = re.match('^He.*(\d+).*Demo$', content)
# 贪婪匹配,会把最后一个数字匹配到括号里
print(result)
print(result.group(1)) # 返回匹配结果

非贪婪匹配
与贪婪匹配对应,重点在一个问号的区别。
import re
content = 'Hello 1234567 World_This is s Regex Demo'
result = re.match('^He.*?(\d+).*Demo$', content)
# 非贪婪匹配,尽可能匹配更少的字符,一遇到数字马上停止,转而把数字匹配到括号中去
print(result)
print(result.group(1)) # 返回匹配结果

匹配模式
文本中有换行符:
import re
content='''Hello 1234567 World_This
is s Regex Demo
'''
result=re.match('^He.*?(\d+).*?Demo$',content)
print(result)

没有匹配成功,因为.(点号)只能匹配换行符以外的任意字符。
改正,加入第三个参数:匹配模式
import re
content = '''Hello 1234567 World_This
is a Regex Demo
'''
result = re.match('^He.*?(\d+).*?Demo$', content, re.S) # re.S使.匹配换行符在内的所有字符
print(result)
print(result.group(1)) # 返回匹配结果

加入匹配模式之后,可以成功匹配了。
另外还有一些修饰符,在需要的时候也可以使用,如下图:
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响^和$ |
re.S | 使.匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响\w ,\W,\b和\B |
re.X | 该标志通过给与你更灵活的格式以便你将正则表达式写得更易于理解 |
转义
如果待匹配的文本中含有特殊字符(如$),那么直接在match中写入包括这个特殊字符在内的表达式是不行的。
import re
content='price is $5.00'
result=re.match('price is $5.00',content)
print(result)

解决:在特殊字符前加反斜杠:"\"。如下:
import re
content='price is $5.00'
result=re.match('price is \$5\.00',content)
print(result)

总结
尽量使用泛匹配、使用括号得到匹配目标、尽量使用非贪婪模式、有换行符就用re.S。
3 re.search
-
match ()方法是从字符串的开头开始匹配的,一旦开头不匹配,那么整个匹配就失败了 ,因此match()方法有比较大的限制。
-
re.search会扫描整个字符串,并且返回第一个成功的匹配,而不管开头是不是一样的。
import re
content = "Extra stings Hello 1234567 World_Thisis a Regex Demo Extra stings"
result = re.search('Hello.?(\d+).?Demo', content)
print(result)
print(result.group(1)) # 返回匹配结果

总结
为了匹配方便,能用search就不用match
匹配练习
下面是一段html代码,里面是一些歌的列表。我们想要获取active标签下的歌名和歌手信息。我们用re.search()方法来匹配
import re
html = '''<div id="songs-list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list"class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3"singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4"class="active">
<a href="/3.mp3"singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3"singer="begoud">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3"singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3"singer="邓丽君"><li class="fa fa-user">但愿人长久</a>
</li>
</ul>
</div>'''
result = re.search('<li.*?active.*?singer="(.*?)">(.*?)</a>', html, re.S)
if result:
print(result.group(1), result.group(2))

上面的基础上,尝试匹配第二个标签,那么把匹配字段中的active去掉就可以了,这样第一次匹配到的就是第二个标签中的内容了:
result=re.search('<li.*?singer="(.*?)">(.*?)</a>',html,re.S)

把re.S去掉会匹配到什么结果?
result=re.search('<li.*?singer="(.*?)">(.*?)</a>',html)

从运行结果可以看到,直接匹配到了第四个标签中的内容。
这是因为不加re.S的话,表达式中的点号是不能匹配换行符的,而前几个标签,在起始的"li"与结束的""直接都存在的换行符,因此无法被匹配到。
4.re.findall
搜索字符串,以列表形式返回全部能匹配的子串。
import re
html = '''<div id="songs-list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list"class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3"singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4"class="active">
<a href="/3.mp3"singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3"singer="begoud">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3"singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3"singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>'''
results = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', html, re.S)
# href表示超链接
# findall的返回值是按()中所标识的字段来匹配的。比如这条语句中就有三个()。
print(results)
print(type(results)) # 看一下结果的类型
for result in results: # 循环打印出列表中的每一个元素
print(result)
print(result[0], result[1], result[2]) # 列表中每一个元素都是一个元组,一个元组里面有三个元素

5 re.sub
替换字符串中每一个匹配的子串后返回替换后的字符串。
第一个参数是表达式,第二个参数为替换成什么字符串(可以为空),第三个参数是原字符串。
将原字符串中的数字替换为空格:
import re
content='Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'
content=re.sub('\d+','',content)
print(content)

替换为指定的字符串:
import re
content = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'
content = re.sub('\d+', 'Replacement', content)
print(content)

要替换成的内容,又包含了原来的内容:
import re
content = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'
content = re.sub('(\d+)', r'\1 8910', content)
# \1表示第一个括号中的内容,前面加r表示\不会被转义
print(content)

上面那个歌曲列表的例子,想获取所以li节点的歌名,直接用正则表达式来提取可能会比较麻烦。我们可以先用sub方法把里面的标签都替换为空,再用findall方法就可以很方便地提取了:
import re
html = '''<div id="songs-list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list"class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3"singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4"class="active">
<a href="/3.mp3"singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3"singer="begoud">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3"singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3"singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>'''
html = re.sub('<a.*?>|</a>', '', html)
# 把两种形式的a标签替换为空
print(html)
results = re.findall('<li.*?>(.*?)</li>', html, re.S)
print(results)
for result in results:
print(result.strip()) # 把换行符去掉

6 re.compile
将正则字符串编译成正则表达式对象,以便复用该匹配模式。
import re
content = '''Hello 1234567 World_This
is a Regex Demo'''
pattern = re.compile('Hello.*Demo', re.S)
# 编译成一个对象
result = re.match(pattern, content)
# 原来的写法:result=re.match('Hello.*Demo',content,re.S)
print(result)
