python -- re正则模块

时间:2021-11-04 03:44:34

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 匹配字符并替换