Python正则表达式
0 参考资料
https://docs.python.org/zh-cn/3/library/re.html
https://bbs.huaweicloud.com/blogs/281158
https://cloud.tencent.com/developer/article/1769462
https://tool.oschina.net/regex/#
1 常用函数
1.1 re.match
语法:re.match(pattern, string, flags=0)
从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。匹配成功re.match方法返回一个匹配的对象。
| pattern | 匹配的正则表达式 |
|---|---|
| string | 要匹配的字符串。 |
| flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。 |
flags 修饰符
'''
修饰符 描述
re.I 使匹配对大小写不敏感
re.L 做本地化识别(locale-aware)匹配
re.M 多行匹配,影响 ^ 和 $
re.S 使 . 匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。
'''
如果上⼀步匹配到数据的话,可以使⽤group⽅法来提取数据。以使用group(num) 或groups()匹配对象函数来获取匹配表达式。
| group(num=0) | 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 |
|---|---|
| groups() | 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。 |
import re
match_result = re.match(r'p','peanut')
match_result.group() # 'p'
match_result1 = re.match(r'.','\n',re.S)
match_result1 #<re.Match object; span=(0, 1), match='\n'>
match_result2 = re.match(r'\nA','\na',re.I)
match_result2 # <re.Match object; span=(0, 2), match='\na'>
1.2 compile方法
- compile 可以把一个字符串编译成字节码
- 优点:在使用正则表达式进行match的操作时,python会将字符串转为正则表达式对象,
- 而如果使用compile只需要一次转换,以后再使用模式对象的话 无需转换。
import re
pattern = re.compile(r'\n')
pattern.match('\n').group() # '\n'
1.3 search 方法
- search :在全文中匹配一次,匹配到就返回
- 语法:re.search(pattern, string, flags=0)
| 参数 | 描述 |
|---|---|
| pattern | 匹配的正则表达式 |
| string | 要匹配的字符串。 |
| flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。 |
re.search(r'h','hello hi',re.I) # <re.Match object; span=(0, 1), match='h'>
re.search(r'.+','\n\n',re.S).group() # '\n\n' re.S 使.能够匹配换行符
1.4 findall方法
- finall():查询字符串中某个正则表达式全部的非重复出现的情况 返回是一个符合正则表达式的结果列表
- 语法:findall(string[, pos[, endpos]])
| 参数 | 描述 |
|---|---|
| string | 待匹配的字符串。 |
| pos | 可选参数,指定字符串的起始位置,默认为 0。 |
| endpos | 可选参数,指定字符串的结束位置,默认为字符串的长度。 |
re.findall(r'h\w{1,}','hello hi hey') # ['hello', 'hi', 'hey']
re.findall(r'h\w{1}','hello hi hey') # ['he', 'hi', 'he']
1.5 sub方法
- sub:将匹配到的数据进行替换,实现目标的搜索和查找
- 语法:sub(pattern, repl, string, count=0, flags=0)
| 参数 | 描述 |
|---|---|
| pattern | 正则中的模式字符串。 |
| repl | 替换的字符串,也可为一个函数。 |
| string | 要被查找替换的原始字符串。 |
| count | 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。 |
| flags | 标志位,用于控制正则表达式的匹配方式 |
import re
re.sub(r'[a-zA-Z0-9]+','python','c是我最喜欢的语言!!!') # 'python是我最喜欢的语言!!!'
import re
# subn实现目标的搜索和替换,返回被替换的数量。
re.subn(r'[a-zA-Z0-9]+','python','c是我最喜欢的语言!!!')
# ('python是我最喜欢的语言!!!', 1)
1.6 split方法
- split:实现分割字符串,以列表形式返回
- 语法:split(pattern, string, maxsplit=0, flags=0)
| 参数 | 描述 |
|---|---|
| pattern | 匹配的正则表达式 |
| string | 要匹配的字符串。 |
| maxsplit | 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。 |
| flags | 标志位,用于控制正则表达式的匹配方式 |
re.split(r'[, ]','hello, my honey!') # ['hello', '', 'my', 'honey!']
2 常见用法
2.1 匹配字符
| 符号 | 匹配规则 |
|---|---|
| .(点) | 匹配任意1个字符除了换行符\n |
| [abc ] | 匹配abc中的任意一个字符 |
| \d | 匹配一个数字,即0-9 |
| \D | 匹配非数字,即不是数字 |
| \s | 匹配空白,即空格,tab键 |
| \S | 匹配非空白,除空格,tab键之类的 |
| \w | 匹配单词字符,即a-z、A-Z、0-9、_ |
| \W | 匹配非单词字符 |
| \b | 匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。 |
| \B | 匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。 |
import re
# .点的使用,匹配除了换行符之外的任意一个字符字符,还可以.*输出后面的字符串
data = 'peanut'
re.match(r'..',data).group() # 'pe'
data = ['peanut','pool','python']
result_lst = [re.match(r'p.',i).group() for i in data]
result_lst
# ['pe', 'po', 'py']
# [] 中括号:匹配中括号中的任意一个字符
data = ['hello','ehllo','h']
result_lst = [re.match(r'[eh]',i).group() for i in data]
result_lst # ['h', 'e', 'h']
data = ['aA0','Aa1','1aA']
result_lst = [re.match(r'[a-zA-Z0-9]',i).group() for i in data]
result_lst # ['a', 'A', '1']
# \d匹配一个数字,即0-9
data = ['43号','9月','1点']
result_lst = [re.match(r'\d+',i).group() for i in data]
result_lst # ['43', '9', '1']
# \D匹配非数字,即不是数字
data = ['月亮20','?\nhello12',' __']
result_lst = [re.match(r'\D+',i).group() for i in data]
result_lst # ['月亮', '?\nhello', ' __']
# \s匹配空白,即空格,tab键
data = [' 这是空格','\nhello','\thello']
result_lst = [re.match(r'\s+',i).group() for i in data]
result_lst # [' ', '\n', '\t']
# \w 匹配单词字符,即a-z、A-Z、0-9、_
data = ['abc__','A_Bc12','a\n1']
result_lst = [re.match(r'\w+',i).group() for i in data]
result_lst # ['abc__', 'A_Bc12', 'a']
2.2 匹配分组
| 符号 | 匹配规则 |
|---|---|
| | | 匹配左右任意一个表达式 |
| (ab) | 将括号中字符作为一个分组 |
| \num | 引用分组num匹配到的字符串 |
| (?P) | 分组起别名 |
| (?P=name) | 引用别名为name分组匹配到的字符串 |
| (?:pattern) | 匹配pattern但不获取匹配的子字符串(shy groups),也就是说这是一个非获取匹配,不存储匹配的子字符串用于向后引用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。 |
# 1. |
data = ['hello','ehllo','h']
result_lst = [re.match(r'h|e',i).group() for i in data]
result_lst
# ['h', 'e', 'h']
data = ['hello','hi','hear']
result_lst = [re.match(r'he|hi',i).group() for i in data]
result_lst
# ['he', 'hi', 'he']
string = 'zood food z'
re.findall(r'z|food',string) # ['z', 'food', 'z']
string = 'zood food z'
re.findall(r'(?:z|f)ood',string) # ['zood', 'food']
# 2. (ab)
data = ['hello hey','hi hieracy','hear he']
result_lst = [re.match(r'(he|hi)',i).group() for i in data]
result_lst
# ['he', 'hi', 'he']
# 3. \num
data = '<html><h1>测试数据</h1></html>'
match_result = re.match(r'<(.+)><(.+)>(.+)</\2></\1>',data)# 因为对象中有转义字符,所以要在前面加r
## </\2></\1>的意思是:在匹配过程中可以引用<(.+)><(.+)>中的字符串
print(match_result.group())
print(match_result.group(1))
print(match_result.group(2))
print(match_result.group(3))
'''
<html><h1>测试数据</h1></html>
html
h1
测试数据
'''
# 4.分组起别名
## (?P<name>) (?P=name) 一个用于标记,一个用于在同一个正则表达式中复用
data = '<html><h1>测试数据</h1></html>'
match_result = re.match(r'<(?P<html>\w*)><(?P<h1>\w*)>(.+)</(?P=h1)></(?P=html)>',data)
print(match_result.group())
print(match_result.group(1))
print(match_result.group(2))
print(match_result.group(3))
'''
<html><h1>测试数据</h1></html>
html
h1
测试数据
'''
2.3 限定匹配字符规则
| 符号 | 匹配规则 |
|---|---|
| * | 匹配前一个字符出现0次或者无限次,即可有可无 |
| + | 匹配前一个字符出现1次或者无限次,即至少有1次 |
| ? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
| 匹配前一个字符出现m次 | |
| 匹配前一个字符至少出现m次 | |
| 匹配前一个字符出现从n到m次 |
# * 匹配前一个字符出现0次或者无限次,即可有可无
data = ['abc__','A_Bc12','\n1']
result_lst = [re.match(r'\w*',i).group() for i in data]
result_lst # ['abc__', 'A_Bc12', ''] # data = ['abc__','A_Bc12','1\n1']
# + 匹配前一个字符出现1次或者无限次,即至少有1次
data = ['abc__','A_Bc12','1\n1']
result_lst = [re.match(r'\w+',i).group() for i in data]
result_lst # ['abc__', 'A_Bc12', '1']
# ? 匹配前一个字符出现1次或者0次,即要么有1次,要么没有
data = ['abc__','\n1','1\n1']
result_lst = [re.match(r'\w?',i).group() for i in data]
result_lst # ['a', '', '1']
# {n,m}匹配前一个字符出现从n到m次
email = '1477547082@qq.com'
email_match = re.match(r'[a-zA-Z0-9]{6,11}@qq.com',email)
email_match.group() # 1477547082@qq.com
2.4 匹配开头和结尾
| 符号 | 匹配规则 |
|---|---|
| ^ | 匹配字符串开头 |
| $ | 匹配字符串结尾 |
data = ['aA0','Aa1','1aA']
result_lst = [re.match(r'^[aA1]',i).group() for i in data]
result_lst # ['a', 'A', '1']
data = ['aA0','Aa1','1aA']
result_lst = [re.match(r'\w{2,}$',i).group() for i in data]
result_lst # ['aA0', 'Aa1', '1aA']
2.5 零宽断言
有时候在使用正则表达式做匹配的时候,我们希望匹配一个字符串,这个字符串的前面或后面需要是特定的内容,但我们又不想要前面或后面的这个特定的内容,这时候就需要零宽断言的帮助了。所谓零宽断言,简单来说就是匹配一个位置,这个位置满足某个正则,但是不纳入匹配结果的,所以叫“零宽”,而且这个位置的前面或后面需要满足某种正则。
2.5.1 正预测先行断言:(?=exp)
匹配一个位置(但结果不包含此位置)之前的文本内容,这个位置满足正则exp,举例:匹配出字符串string中以s结尾的单词的前半部分
string = "Things are good! \nApples are fruits!"
re.findall(r'\b\w+(?=s\b)',string) # ['Thing', 'Apple', 'fruit']
2.5.2 正回顾后发断言:(?<=exp)
匹配一个位置(但结果不包含此位置)之后的文本,这个位置满足正则exp,举例:匹配出字符串string中以h开头的单词的后半部分.
string = 'hello, my honey! I think you are hungry!'
re.findall(r'(?<=\bh)\w+\b',string) # ['ello', 'oney', 'ungry']
2.5.3 负预测先行断言:(?!exp)
匹配一个位置(但结果不包含此位置)之前的文本,此位置不能满足正则exp,举例:匹配出字符串string中不以ing结尾的单词的前半部分.负向断言不支持匹配不定长的表达式
string = 'done do doing'
re.findall(r'\b\w{3}(?!ing\b)',string) # ['don', 'doi']
string = 'done do doing'
re.findall(r'\b\w{2}(?!ing\b)',string) # ['do', 'do']
string = 'done do doing'
re.findall(r'\w{2}',string) # ['do', 'ne', 'do', 'do', 'in']
2.5.4 负回顾后发断言:(?<!exp)
匹配一个位置(但结果不包含此位置)之后的文本,这个位置不能满足正则exp,举例:匹配字符串s中不以do开头的单词.
string = 'done do going'
re.findall(r'(?<!\bd)\w{5}\b',string) # ['going']
2.5.5 结合使用
# 字符串ip是一个ip地址,现在要匹配出其中的四个整数
ip = '192.168.0.1'
re.findall(r'(?<=\.)?\d+(?=\.)?',ip) # ['192', '168', '0', '1']
# 匹配字符串s中的一些单词,这些单词不以’z’开头且不以’i’结尾
string = 'zoai xiay zbci zjai'
print(re.findall(r'\w{3}',string))
print(re.findall(r'(?<!\bz)\w{3}(?!i\b)',string))
'''
['zoa', 'xia', 'zbc', 'zja']
['xia']
'''
# 匹配一个长度为4个字符的字符串,该字符串只能由数字、字母或下划线3种字符组成,且必须包含其中的至少两种字符,且不能以下划线或数字开头
# 测试数据
strs = ['_aaa','1aaa','aaaa','a_12','a1','a_123','1234','____']
p = re.compile(r'^(?!_)(?!\d)(?!\d+$)(?![a-zA-Z]+$)\w{4}$')
for s in strs:
print (re.findall(p,s))
'''
[]
[]
[]
['a_12']
[]
[]
[]
[]
'''
3 贪婪与非贪婪
默认条件下为贪婪模式
- 贪婪:在满足条件情况下尽可能匹配到数据
- 非贪婪:满足条件就可以,在"*","?","+","{m,n}"后面加上?,就能将贪婪变成非贪婪.
# 贪婪模式
pattern1 = re.compile(r'a.*b')
pattern1.search('aabcbcd').group() # 'aabcb'
# 非贪婪模式
pattern2 = re.compile(r'a.*?b')
pattern2.search('aabcbcd').group() # 'aab'
4 r 的作用
正则表达式里使用”\”作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符”\”,那么使用编程语言表示的正则表达式里将需要4个反斜杠”\\”:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,Python中字符串前⾯加上 r 表示原⽣字符串。
import re
data = 'c:\\a\\b\\c'
print(data)
print(re.search('c:\\\\a',data).group())
print(re.search(r'c:\\a',data).group())
'''
c:\a\b\c
c:\a
c:\a
'''
5 常见用法
5.1 匹配汉字
pattern = re.compile(r"[\u4e00-\u9fa5]")
strs = 'peanut是花生的意思~~~'
result = pattern.findall(strs)
print(result) # ['是', '花', '生', '的', '意', '思']

浙公网安备 33010602011771号