python -- re正则模块
什么是正则?
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE)。正则是用于做字符串匹配,进行字符串操作。
- 可以为想要匹配的相应字符串集指定规则
- 该字符串集可能包含英文语句、email地址、命令或任何你想搞定的东西
- 可以问诸如:这个字符串匹配该模式吗?
- “在这个字符串中是否有部分匹配该模式?”
- 也可以使用RE以各种方式来修改或分割字符串。
正则表达式
正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行。
正则表达式,由一些普通字符和一些元字符(metacharacters)组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义。
- 普通字符
- 大多数字母和字符一般都会和 自身匹配,例如正则表达式test会和字符串“test”完全匹配
- 元字符metacharacters,为了模糊查询
- ^ $ * + ? {} [] | ()
正则表达式 - 元字符
- [ ]
- 常用来指定一个字符集:[abc];[a-z],[abc]是匹配a或b或c,[a-z]是匹配从a~z,一个一个匹配
- 元字符在字符集中不起作用:[abc$],这里的$就不再表示以c结尾,而表示为$也是匹配对象之一
- 补集匹配不在区间范围内的字符:[^f],表示处f以外的字符集
import re
s = r'[a-z]'
s_1 = r'[a-z]{2}'
s_2 = r'[^a-z]'
s_3 = r'[ab$]'
s_4 = r'[a-z^]'
st = 'aa00acdab'
st_1 = 'aa00acdab^'
res = re.findall(s, st)
res_1 = re.findall(s_1, st)
res_2 = re.findall(s_2, st)
res_3 = re.findall(s_3, st)
res_4 = re.findall(s_4, st_1)
print(res)
print(res_1)
print(res_2)
print(res_3)
print(res_4)
'''
以下是程序的结果
['a', 'a', 'a', 'c', 'd', 'a', 'b']
['aa', 'ac', 'da']
['0', '0']
['a', 'a', 'a', 'a', 'b']
['a', 'a', 'a', 'c', 'd', 'a', 'b', '^']
'''
- ^
- 匹配行首,只有在设置了re.M时候,它才会匹配多行,不然就相当于起始位置。
- $
- 匹配行尾, 行尾被定义为 字符串尾,或一个换行字符后面的任何位置
import re
s = r'^hello'
s_1 = r'rld$'
st = 'hello world,hello world'
st_1 = '''hello world;
hello world
hello world'''
res = re.findall(s, st)
res_1 = re.findall(s, st_1)
res_2 = re.findall(s, st_1, re.M)
res_3 = re.findall(s_1, st)
print(res) # ['hello']
print(res_1) # ['hello']
print(res_2) # ['hello', 'hello', 'hello']
print(res_3) # ['rld']
-
- 反斜杠后面可以加不同的字符表示不同特殊意义
- 也可以用于取消所有的元字符;\[或 \ - \d 匹配任何十进制,同[0-9]
- \D 匹配任何非数字,同[^0-9]
- \s 匹配任何空白字符,同[\t\n\r\f\v]
- \S 匹配任何非空白字符,同[^\t\n\r\f\v]
- \w 匹配任何字母数字,下划线,同[a-zA-Z0-9_]
- \W 匹配任何非字母数字,下划线,同^a-zA-Z0-9_]
正则表达式 - 元字符 重复
正则表达式第一功能是能够匹配不定长的字符集,另一功能就是可以指定正则表达式的一部分的重复次数。
-
-
- 指定前一个字符可以被匹配0次或多次,不止一次。
-
-
-
- 匹配一次或更多次
- 与*的区别,*允许匹配0次,+
-
- ?
- 匹配一次或0次,可有可无,用于标记某事物是可选。可以适用于正则中贪婪模式和非贪婪模式。
- {m,n}
- m 和n是十进制整数, 至少m次重复,最多n次重复。
- 忽略m下边界是0,忽略n上边界为无穷的(20亿)
- {0,},等同于*,{1,}等同于+,而{0,1},等同于? 不建议这样使用,只做理解用。
import re
r = r'ab*'
r_1 = r'ab+'
res = re.findall(r, 'a')
res_1 = re.findall(r, 'ab')
res_2 = re.findall(r, 'abbbb')
res_3 = re.findall(r, 'bbbb')
Res = re.findall(r_1, 'a')
Res_1 = re.findall(r_1, 'ab')
Res_2 = re.findall(r_1, 'abbbb')
Res_3 = re.findall(r_1, 'bbbb')
print(res) # ['a']
print(res_1) # ['ab']
print(res_2) # ['abbbb']
print(res_3) # []
print(Res) # []
print(Res_1) # ['ab']
print(Res_2) # ['abbbb']
print(Res_3) # []
正则的贪婪和非贪婪模式
用 问号 ?实现
import re
r = r'ab+' # 贪婪模式
r_1 = r'ab+?' # 非贪婪模式
st = 'abbbbbbbb'
print(re.search(r, st).group()) # abbbbbbbb
print(re.search(r_1, st).group()) # ab
import re
a = 'inet 地址:192.168.12.55 广播:192.168.12.255'
a_1 = 'abcabc123123def456def456'
a_2 = '310104198001011211'
# 可以写成re.match(*******).group()
# re.match从头开始匹配
b = re.match('192', a)
# b.group()
c = re.match('inet', a)
print(c.group()) # inet
# \w只取一个字符,匹配[A-Za-z0-9],不匹配特殊字符
d = re.match('\w', a)
print(d.group()) # i
# \w+只取一个字符,匹配[A-Za-z0-9],匹配前一个字符1次或多次
e = re.match('\w+', a)
print(e.group()) # inet
# .只取一个字符,默认匹配除\n之外的任意一个字符,要多次匹配,只要加+即可
f = re.match('.', a)
f_1 = re.match('.+', a)
print(f.group()) # i
print(f_1.group()) # inet 地址:192.168.12.55 广播:192.168.12.255
# 匹配*号前的字符0次或多次,可以配不到
g = re.match('ab*', a_1)
print(g.group()) # ab
# 匹配?号前一个字符1次或0次
h = re.match('a?', a_1)
print(h.group()) # a
# {m}匹配前一个字符m次
i = re.match('\w{5}', a_1)
print(i.group()) # abcab
# 匹配|左或|右的字符
j = re.match('abc|ABC', a_1)
print(j.group()) # abc
# 查询身份证的套路
l = re.search('(\d{2})(\d{2})(\d{2})(\d{4})', a_2)
print(l.groups()) # ('31', '01', '04', '1980')
# 匹配$前结尾,或用\Z一样,例如:以数字开头,以数字结尾的套路
m = re.search('\d.*\d$', a_2)
m_1 = re.search('\d.*\d\Z', a_2)
print(m.group()) # 310104198001011211
print(m_1.group()) # 310104198001011211
# '(?P<name>...)' 分组匹配 ,比较好玩
n = re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})",a_2)
print(n.groupdict()) # {'province': '3101', 'city': '04', 'birthday': '1980'}
print(n.groups()) # ('3101', '04', '1980')
# 开始对a变量,取IP地址了
# p = re.search("(\d{3}\.){3}\d{3}", a), 这个语句和下面的区别是{3}一定要匹配3次,{1,3}是匹配1次或2次或3次
p = re.search("(\d{1,3}\.){3}\d{1,3}", a)
print(p.group()) # 192.168.12.55
# 取出所有数字,用列表表示
q = re.findall('\d+', a_1)
print(q) # ['123123', '456', '456']
r = re.findall('[a-zA-Z]+', a_1)
r_1 =re.findall('\D+', a_1)
r_2 = re.split('\d+', a_1)
print(r) # ['abcabc', 'def', 'def']
print(r_1) # ['abcabc', 'def', 'def']
print(r_2 ) # ['abcabc', 'def', 'def', '']
# 用数字为分割,用|取代数字,替换的功能
s = re.sub('\d+', '|', a_1)
s_1 =re.sub('\d+', '|', a_1, count=2)
print(s) # abcabc|def|def|
print(s_1) # abcabc|def|def456
# 较为烦人的反斜杠\
t = re.split('\\\\', r'c:\users\username\desktop')
print(t)
re.compile() 正则编译 将正则表达式转成对象
re模块提供了一个正则表达式引擎的接口,可以将REstring编译成对象并用它们进行匹配。编译后的正则速度比非编译的快很多。
什么时候用到正则编译?
当某个正则用的地方较多是,可以考虑用正则编译
import re
# 定义一个正则规则
r = r'\d{3,4}-?\d{8}'
# 定义一个正则编译
p_tel = re.compile(r)
# 使用p_tel正则编译
print(p_tel.findall('021-123456789')) # ['021-12345678']
# 定义一个不区分大小写的正则编译
sfsc_re = re.compile(r'sfsc', re.I)
print(sfsc_re.findall('SFsc')) # ['SFsc']
取手机号码
import re
r = '(1[3,8])(?=([0-9]{9}))'
s = 'fafafa1381867845245813800000000fff'
info = re.findall(r, s)
new_info =[]
for i in range(len(info)):
s = info[i][0]+info[i][1]
new_info.append(s)
print(new_info)
反斜杠的困扰
用四个\来解析一个,即‘\\\\’来转移一个
re.match() 判断匹配是否成功,一般只用来判断有值还是没有值
match的常用用法:
import re
# 定义一个不区分大小写的正则编译
sfsc_re = re.compile(r'sfsc', re.I)
x = sfsc_re.match('uuuww')
# x = sfsc_re.match('sfsc uuuww')
if x:
print(' x is not empty')
elif not x:
print('x is null')
编译标志-flags
- re.S 使.匹配包含换行\n在内的所有字符
- re.I 忽略匹配的大小写
- re.I 做本地化识别,类似法语,西班牙语等
- re.M 多行匹配,影响^和$
- re.X 当正则表达式是多行时使用
正则表达式 分组
分组:“(”和“)”
利用分组()提取正则的效果
分组有优先处理正则的效果,所以可以用这一特性来实现一些功能。
import re
email = r'\w{3}@\w+(\.com|\.cn)'
res = re.match(email, 'zzz@sfsc.com').group()
print(res) # zzz@sfsc.com
res = re.match(email, 'zzz@sfsc.cn').group()
print(res) # zzz@sfsc.cn
# 奇迹发生了,接下来是见证奇迹的时刻
res = re.findall(email, 'zzz@sfsc.com')
print(res) # ['.com']
res = re.search(email, 'zzz@sfsc.com').group()
print(res) # zzz@sfsc.com
import re
s = '''
hhsdj dskj hello src=oldboy yes jdjsds
guhihjj src=123 yes jkjl
src=443 yes
hello src=python yes nkjn'''
# 定义一个正则表达式
r = 'hello src=.+ yes'
res = re.findall(r, s)
print(res) # ['hello src=oldboy yes', 'hello src=python yes']
# 利用分组的概念只提起res中src=的值。
r_1 = 'hello src=(.+) yes'
res_1 = re.findall(r_1, s)
print(res_1) # ['oldboy', 'python']
写在后面
- 定义一个正则表达式,就等于顶一个字符串
常用正则表达式符号 | 备注 |
---|---|
'.' | 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 |
'^' | 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE) |
'$' | 匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也可以 |
'*' | 匹配*号前的字符0次或多次,re.findall("ab*","cabb3abcbbac") 结果为['abb', 'ab', 'a'] |
'+' | 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb'] |
'?' | 匹配前一个字符1次或0次 |
'{m}' | 匹配前一个字符m次 |
'{m,}' | 匹配前一个字符至少m次,类似m+ |
'{n,m}' | 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb'] |
'[xyz]' | 字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a” |
'[^xyz]' | 负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”。 |
'[a-z]' | 字符范围。匹配指定范围内的任意字符。 |
'|' | 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC' |
'(...)' | 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c |
'\A' | 只从字符开头匹配,有点类似'^'' re.search("\Aabc","alexabc") 是匹配不到的 |
'\Z' | 匹配字符结尾,同$ |
'\d' | 匹配数字0-9 |
'\D' | 匹配非数字 |
'\w' | 匹配[A-Za-z0-9] |
'\W' | 匹配非[A-Za-z0-9],等价于“[^A-Za-z0-9_]只匹配特殊字符 |
'\s' | 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t' |
'(?P...)' | 分组匹配 re.search("(?P[0-9]{4})(?P[0-9]{2})(?P[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'} |
常用方法 | 备注 |
---|---|
re.match | 从头开始匹配,一般不怎么用,search较多用 |
re.group | 取出匹配的内容 |
re.search | 匹配包含,用的较多 |
re.findall | 把所有匹配到的字符放到以列表中的元素返回 |
re.split | 以匹配到的字符当做列表分隔符 |
re.sub | 匹配字符并替换 |