Python 正则库学习笔记

1 要记得的一些常用匹配模式

  • \w 匹配字母,数字,和下划线
  • \W 匹配非字母数字,和下划线
  • \s 匹配任意空白字符,例如[\t\n\r\f],其中”\t”是制表符
  • \S 匹配任意非空白字符
  • \d 匹配任意数字,等价于[0-9]
  • ^ 匹配字符串开头
  • $ 匹配字符串结尾
  • . 匹配任意字符,除了换行符
  • * 匹配0个或者多个的表达式
    • 匹配1个或者多个的表达式
  • ? 匹配0个或者1个前面的表达式,非贪婪模式
  • {n} 匹配前面的表达式n次
  • {n,m} 匹配前面的表达式n到m次,贪婪模式

2 python的re库

match()方法,一定要从头开始才匹配

  1. match() 方法会尝试从字符串的起始位置匹配正则表达式,如果匹配,就返回匹配成功的结果,如果不匹配,那就返回 None。match() 方法是从字符串的开头开始匹配,一旦开头不匹配,那么整个匹配就失败了。
import re

content = 'Hello 123 4567 World_This is a Regex Demo'

print(len(content))

result=re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}',content) #第一个参数是正则,第二个参数是目标字符串

print(result) #返回一个结果对象:SRE_Match 对象
print(result.span()) #返回匹配的字符串起始位置
print(result.group()) # 返回匹配的结果字符串

#output:
41
<_sre.SRE_Match object; span=(0, 25), match='Hello 123 4567 World_This'>
(0, 25)
Hello 123 4567 World_This
()
  1. 在这里可以使用 () 括号来将我们想提取的子字符串括起来,() 实际上就是标记了一个子表达式的开始和结束位置,被标记的每个子表达式会依次对应每一个分组,我们可以调用 group() 方法传入分组的索引即可获取提取的结果。
import re

content = 'Hello 123 4567 World_This is a Regex Demo'

print(len(content))

result=re.match('^Hello\s\d\d\d\s(\d{4})\s\w{10}',content)

print(result.group(1)) #
print(result.groups())

#output:
41
4567
('4567',)
  1. 通用匹配,改进上面的正则表达式

一个万能匹配可以用,也就是 .* (点星),.(点)可以匹配任意字符(除换行符),*(星) 又代表匹配前面的字符无限次,所以它们组合在一起就可以匹配任意的字符了,有了它我们就不用挨个字符地匹配了。

import re

content = 'Hello 123 4567 World_This is a Regex Demo'

print(len(content))

result=re.match('^Hello.*Demo$',content) #使用了.\* (点星)进行通用匹配

print(result.group())

#output:

41
Hello 123 4567 World_This is a Regex Demo
()

4.贪婪与非贪婪

这里就涉及一个贪婪匹配与非贪婪匹配的原因了,贪婪匹配下,.* 会匹配尽可能多的字符,我们的正则表达式中 .* 后面是 \d+,也就是至少一个数字,并没有指定具体多少个数字,所以 .* 就尽可能匹配多的字符,所以它把 123456 也匹配了,给 \d+ 留下一个可满足条件的数字 7,所以 \d+ 得到的内容就只有数字 7 了。+

import re

content = 'Hello 123 4567 World_This is a Regex Demo'

print(len(content))

result=re.match('^He.*(\d+).*Demo$',content) #贪婪模式

print(result.group())

print(result.groups())

贪婪匹配是尽可能匹配多的字符,非贪婪匹配就是尽可能匹配少的字符,.*? 之后是 \d+ 用来匹配数字,当 .*? 匹配到 Hello 后面的空白字符的时候,再往后的字符就是数字了,而 \d+ 恰好可以匹配,那么这里 .*? 就不再进行匹配,交给 \d+ 去匹配后面的数字。所以这样,.*? 匹配了尽可能少的字符,\d+ 的结果就是 1234567 了

import re

content = 'Hello 1234567 World_This is a Regex Demo'

print(len(content))

result=re.match('^He.*?(\d+).*Demo$',content) #非贪婪模式

print(result.group())

print(result.groups())

#output:

40
Hello 1234567 World_This is a Regex Demo
('1234567',)

实验证明,使用 .*? (点星问号) 和 .+?(点加问号)可以实现非贪婪模式匹配,在字符串中间尽量用非贪婪模式匹配,在字符串结尾用贪婪模式匹配

5 修饰符(match方法的第三个参数)

import re

content = '''Hello 1234567 World_This
is a Regex Demo
'''
result = re.match('^He.*?(\d+).*?Demo$', content,re.S) #增加了re.S,使得(.*?)可以匹配换行符
print(result.group(1))

search()方法,匹配第一个结果

match() 方法是从字符串的开头开始匹配,一旦开头不匹配,那么整个匹配就失败了,search()方法在匹配时会扫描整个字符串,然后返回第一个成功匹配的结果,也就是说,正则表达式可以是字符串的一部分,在匹配时,search() 方法会依次扫描字符串,直到找到第一个符合规则的字符串,然后返回匹配内容,如果搜索完了还没有找到,那就返回 None,为了匹配方便,我们可以尽量使用 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="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君"><i class="fa fa-user"></i>但愿人长久</a>
</li>
</ul>
</div>'''

result=re.search('<li.*?4.*?active.*?singer="(.*?)">(.*?)</a>',html,re.S)
print(result.group())
print(result.group(1))
print(result.group(2))


#output:
<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>
齐秦
往事随风

findall()方法,返回列表

在前面我们说了 search()方法的用法,它可以返回匹配正则表达式的第一个内容,但是如果我们想要获取匹配正则表达式的所有内容的话怎么办?这时就需要借助于 findall() 方法了。如果有返回结果的话就是列表类型,所以我们需要遍历一下来获依次获取每组内容。

result=re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>',html,re.S)
# print(result.group())
print(isinstance(result,list))
print(type(result))
print(result)

#output:
True
<class 'list'>
[('/2.mp3', '任贤齐', '沧海一声笑'), ('/3.mp3', '齐秦', '往事随风'), ('/4.mp3', 'beyond', '光辉岁月'), ('/5.mp3', '陈慧琳', '记事本'), ('/6.mp3', '邓丽君', '<i class="fa fa-user"></i>但愿人长久')]

所以,如果只是获取第一个内容,可以用 search() 方法,当需要提取多个内容时,就可以用 findall() 方法。+

sub()方法,用于修改文本,返回修改后的字符串

第一个参数传入正则表达式来匹配需要替换的内容,然后第二个参数是替换成的字符串,要去掉的话就可以赋值为空,第三个参数就是原字符串。

import re

content = '''Hello 1234567 World_This
is a Regex Demo
'''
result = re.sub('\d+','',content,re.S)
print(result)#返回一个字符串

#output:

Hello World_This
is a Regex Demo

compile()方法,提前封装好正则表达式

import re

content1 = '2016-12-15 12:00'

pattern=re.compile('\d{2}:\d{2}')#提前编译好正则表达式,可以提供运行效率

result=re.sub(pattern,'',content1)

print(result)