目录
动机
- 文本处理已经成为一段极常见的工作之一
- 对文本内容的搜索,定位,提取是逻辑比较复杂的工作
- 为了快速方便的解决上述问题,产生了正则表达式技术
简介
-
定义
即文本的高级匹配模式,提供搜索,替换等功能。其本质是由一系列字符和特殊符号构成的字符串,这个字符串就是正则表达式。 -
原理
通过普通字符和有特定含义的字符来组成字符串,用以描述一定的字符串规则,比如重复、位置等,来表达某类特定的字符串,进而匹配。 -
目标
- 熟练掌握正则表达式元字符
- 能够读懂常用的正则表达式,编辑简单的正则规则
- 能够熟练使用re模块操作正则表达式
元字符使用
普通字符
- 匹配规则:每个普通字符匹配其对应的字符
import re
re.findall('正则表达式','字符串')
e.g.
In: re.findall('ab','abcdefabcd')
Out: ['ab','ab']
- 注意事项:正则表达式在python中也可以匹配中文。
或关系
- 元字符:“|”
- 匹配规则:匹配竖线两侧任意的正则表达式即可
e.g.
In: re.findall('com|cn','www.baidu.com')
Out: ['com']
In: re.findall('com|cn','www.taobao.cn')
Out: ['cn']
匹配单个字符
- 元字符:.
- 匹配规则:匹配除换行外的任意一个字符,一个点占一位
e.g.
In: re.findall('王.萌','王小萌,王大萌')
Out: ['王小萌','王大萌']
In: re.findall('张..','张飞,张美丽,张漂亮')
Out: ['张飞,','张美丽','张漂亮']
匹配字符集
- 元字符:[字符集]
- 匹配规则:匹配字符集中的任意一个字符
- 表达形式:
[abc#!好]表示[]中的任意一个字符
[0-9],[a-z],[A-Z]表示区间内的任意一个字符
[_#?0-9a-z]混合书写,一般区间表达写在后面
e.g.
In: re.findall('[ab]','ab')
Out: ['a','b']
In: re.findall('[aeiou]','How are you!')
Out: ['o','a','e','o','u']
In: re.findall('[0-9]','Hello A-007!')
Out: ['0','0','7']
In: re.findall('[-0-9]','Hello A-007!')#范围写在后面,单个零散字符写前面
Out: ['-','0','0','7']
匹配字符集反集
- 元字符:[ ^字符集 ]
- 匹配规则:匹配除了字符集以外的任意一个字符
e.g.
In: re.findall('[^0-9]','Use 007 port')
Out: ['U','s','e',' ',' ','p','o','r','t']
匹配字符串开始位置
- 元字符:^
- 匹配规则:匹配目标字符串的开头位置
e.g.
In: re.findall('^Jame','Hi,Jame')#识别在开头的Jame
Out: []
In: re.findall('^Jame','Jame,Hello')#识别在开头的Jame
Out: ['Jame']
匹配字符串结尾位置
- 元字符:$
- 匹配规则:匹配目标字符串的结尾位置
e.g.
In: re.findall('Jame$','Hi,Jame')#识别在结尾的Jame
Out: ['Jame']
- 规则技巧:^和$必然出现在正则表达式的开头和结尾处。如果两者同时出现,则中间的部分必须匹配整个目标字符串的全部内容(完全匹配)
e.g.
In: re.findall('^Jame$','Hi,Jame')# 完全匹配
Out: ['']
In: re.findall('^Jame$','Jame')# 完全匹配
Out: ['Jame']
匹配字符重复
- 元字符:*
- 匹配规则:匹配前面的字符出现0次或者多次
In: re.findall('wo*','woooooo~~w!')#首字母是w,o可以出现0次或多次
Out: ['woooooo','w']
# 匹配所有大写字母开头的单词
In: re.findall('[A-Z][a-z]*','A Hello World Hi hello kitty')
Out: ['A','Hello', 'World', 'Hi']
- 元字符:+
- 匹配规则:匹配前面的字符出现1次或者多次
In: re.findall('wo+','woooooo~~w!')#首字母是w,o可以出现1次或多次
Out: ['woooooo']
- 元字符:?
- 匹配规则:匹配前面的字符出现0次或者1次
In: re.findall('wo?','w,woooooo')
Out: ['w','wo']
e.g.匹配整数
In: re.findall('-?[0-9]+','Jame,age:18, -26')
Out: ['18','-26']
- 元字符:{n}
- 匹配规则:匹配前面的字符出现n次
e.g.匹配手机号
In: re.findall('1[0-9]{10}','Jame:13812345678')
Out: ['13812345678']
- 元字符:{m,n}
- 匹配规则:匹配前面的字符出现m~n次
e.g.匹配QQ号(6位~11位)
In: re.findall('[1-9][0-9]{5,10}','Baron:1259296994')
Out: ['1259296994']
匹配任意(非)数字字符
- 元字符:\d \D
- 匹配规则:\d 匹配任意数字字符,\D 匹配任意非数字字符
e.g.匹配端口
In: re.findall('\d{1,5}','Mysql:3306,http:80')
Out: ['3306','80']
e.g.匹配所有数字(正数,负数,小数)
In: re.findall('-?\d+\.?\d*','示例段落')
匹配任意(非)普通字符
- 元字符:\w \W
- 匹配规则:\w 匹配普通字符,\W匹配非普通字符
- 说明:普通字符指数字,字母,下划线,汉字
e.g.匹配端口
In: re.findall('\w+','server_port:8888')
Out: ['server_port','8888']
匹配任意(非)空字符
- 元字符:\s \S
- 匹配规则:\s 匹配空字符 \S 匹配非空字符
- 说明:空字符指 空格 \r \n \t \v \f 字符
e.g.
In: re.findall('\w+\s+\w+','hello world')
Out: ['hello world']
In: re.findall('\S+','Tedu-0.1~oo xxxx')
Out: ['Tedu-0.1~oo','xxxx']
匹配开头结尾位置(不常用)
- 元字符:\A(同^) \Z(同$)
- 匹配规则:\A 表示开头位置,\Z 表示结尾位置
匹配(非)单词的边界位置
- 元字符:\b \B
- 匹配规则:\b 表示单词边界,\B 表示非单词边界
- 说明:单词边界指数字、字母(汉字)、下划线与其他字符的交界位置。
e.g.
In: re.findall(r'is','This is a test')
Out: ['is','is']
In: re.findall(r'\bis\b','This is a test')
Out: ['is']#第二个is
In: re.findall(r'\Bis\b','This is a test')
Out: ['is']#第一个is
# 匹配大写字母开头的单词(原始写法)
In: re.findall(r'[A-Z]\w+','This is iPython')
Out: ['This','Python']
# 匹配大写字母开头的单词
In: re.findall(r'\b[A-Z]\w+','This is iPython')
Out: ['This']
正则表达式的转义
- 如果使用正则表达式匹配特殊字符则需要加 \ 表示转义。
特殊字符:. * + ? ^ $ [] () {} | \
e.g. 匹配特殊字符 . 时使用 \. 表示本身含义
In: re.findall('-?\d+\.?\d*','123,-123,1.23,-1.23')
Out: ['123', '-123', '1.23', '-1.23']
- 在编程语言中,常使用原生字符串书写正则表达式避免多重转义的麻烦。
“\\$\\d+” 等同于 r"\$\d+"
贪婪模式和非贪婪模式
- 定义
- 贪婪模式:默认情况下,匹配重复的元字符总是尽可能多的向后匹配内容。比如:* + ? {m,n}
- 非贪婪模式(懒惰模式):让匹配重复的元字符尽可能少的向后匹配内容。
- 贪婪模式转换为非贪婪模式
- 在匹配重复元字符后加 ? 即可
* : *?
+ : +?
? : ??
{m,n} : {m,n}?
e.g. 贪婪模式(括号之间的内容越多越好)
In: re.findall('\(.+\)','(abcd)efgh(ijkl)')
Out: ['(abcd)efgh(ijkl)']
e.g. 非贪婪模式(括号之间的内容越少越好)
In: re.findall('\(.+?\)','(abcd)efgh(ijkl)')
Out: ['(abcd)', '(ijkl)']
正则表达式分组
-
定义:在正则表达式中,以()建立正则表达式的内部分组,子组是正则表达式的一部分,可以作为内部整体操作的对象。
-
作用
- 可以被作为整体操作,改变元字符的操作对象(必须能匹配到内容才能发挥作用,匹配不到内容会报错)
# 注意函数更换了
e.g. 改变+号的重复对象
In: re.search(r'ab+','ababababababab').group()
Out: 'ab'
In: re.search(r'(ab)+','ababababababab').group()
Out: 'ababababababab'
e.g. 改变|号的操作对象
In: re.search(r'(王|李)\w{1,3}','王者荣耀').group()
Out: '王者荣耀'
- 可以通过编程语言某些接口获取匹配内容中,子组对应的内容部分
In: re.search(r'(ab)cd','abcdef').group()
Out: 'abcd'
In: re.search(r'(ab)cd','abcdef').group(1)
Out: 'ab'
- 捕获组
可以给正则表达式的子组起一个名字,表达该子组的意义。这种有名称的子组即为捕获组。
格式:(?P<起的名字>正则表达式)
# 功能相同,就是给正则表达式起了个名字,方便查看正则表达式意义
In: re.search(r'(ab)cd','abcdef').group()
Out: 'abcd'
In: re.search(r'(?P<pig>ab)cd','abcdef').group()
Out: 'abcd'
# 匹配组里面的内容
In: re.search(r'(?P<pig>ab)cd','abcdef').group('pig')
Out: 'ab'
- 注意事项
- 一个正则表达式中可以包含多个子组
- 子组可以嵌套,但是不要重叠或者嵌套结构复杂
- 子组序列号一般从外到内,从左到右计数(group)
正则表达式匹配原则
- 正确性:能够正确的匹配出目标字符串
- 排他性:除了目标字符串之外尽可能少的匹配其他内容
- 全面性:尽可能考虑到目标字符串的所有情况,不遗漏
Python re模块使用
regex = compile(pattern,flags = 0)
功能:生产正则表达式对象
参数:pattern 正则表达式;flags 功能标志位,扩展正则表达式的匹配
返回值:正则表达式对象
compile对象属性:
【1】flags:flags值
【2】pattern:正则表达式
【3】groups:子组数量
【4】groupindex:捕获组名与组序号的字典
re.findall(pattern,string,flags = 0)
功能:根据正则表达式匹配目标字符串内容
参数:pattern 正则表达式;string 目标字符串;flags 功能标志位,扩展正则表达式的匹配
返回值:匹配到的内容列表,如果正则表达式有子组则只能获取到子组对应的内容
import re
s = "Levi:1994,Sunny:1993"
pattern = r"\w+:\d+"
pattern1 = r"(\w+):\d+"
pattern2 = r"(\w+):(\d+)"
l = re.findall(pattern,s) # ['Levi:1994', 'Sunny:1993']
l1 = re.findall(pattern1,s) # ['Levi', 'Sunny']
l2 = re.findall(pattern2,s) # [('Levi', '1994'), ('Sunny', '1993')]
regex.findall(string,pos,endpos)
功能:根据正则表达式匹配目标字符串内容
参数:string 目标字符串;pos截取目标字符串的开始匹配位置;endpos截取目标字符串的结束匹配位置
返回值:匹配到的内容列表,如果正则表达式有子组则只能获取到子组对应的内容
re.split(pattern,string,flags=0)
功能:使用正则表达式匹配内容,切割目标字符串
参数:pattern 正则表达式;string 目标字符串;flags 功能标志位,扩展正则表达式的匹配
返回值:切割后的内容列表
s = "hello world how are you L-body"
l = re.split(r'[^\w]+',s) # 按照非普通字符分割
print(l) # ['hello', 'world', 'how', 'are', 'you', 'L', 'body']
re.sub(pattern,replace,string,max,flags=0)
功能:使用一个字符串替换正则表达式匹配到的内容
参数:pattern 正则表达式;replace 替换的字符串;string 目标字符串;max 最多替换几处,默认替换全部;flags 功能标志位,扩展正则表达式的匹配
返回值:替换后的字符串
s = '时间:2019/10/12'
ns = re.sub(r'/','-',s)
print(ns) # 时间:2019-10-12
ns1 = re.sub(r'/','-',s,1)
print(ns1) # 时间:2019-10/12
re.subn(pattern,replace,string,max,flags=0)
功能:使用一个字符串替换正则表达式匹配到的内容
参数:pattern 正则表达式;replace 替换的字符串;string 目标字符串;max 最多替换几处,默认替换全部;flags 功能标志位,扩展正则表达式的匹配
返回值:替换后的字符串和替换了几处
s = '时间:2019/10/12'
ns = re.sub(r'/','-',s,1)
print(ns) # ('时间:2019-10/12',1)
re.finditer(pattern,string,flags=0)
功能:根据正则表达式匹配目标字符串内容
参数:pattern 正则表达式;string 目标字符串;flags 功能标志位,扩展正则表达式的匹配
返回值:匹配结果的迭代器
s = '2019年,建国70周年'
pattern = r"\d+"
# 返回包含匹配结果的迭代器
it = re.finditer(pattern,s)
for i in it:
print(i.group())
# 2019
# 70
re.fullmatch(pattern,string,flags=0)
功能:完全匹配某个目标字符串(相当于正则表达式两边加上^ $)
参数:pattern 正则表达式;string 目标字符串
返回值:匹配内容match object
re.match(pattern,string,flags=0)
功能:匹配某个目标字符串开始位置(相当于正则表达式两边加上^)
参数:pattern 正则表达式;string 目标字符串
返回值:匹配内容match object
re.search(pattern,string,flags=0)
功能:匹配目标字符串第一个符合内容
参数:pattern 正则表达式;string 目标字符串
返回值:匹配内容match object
m = re.search(r'\S+','好\n嗨 哟')
print(m.group()) # 好
match对象的属性方法
- 属性变量
- pos 匹配的目标字符串开始位置
- endpos 匹配的目标字符串结束位置
- re 正则表达式
- string 目标字符串
- lastgroup 最后一组的名称
- lastindex 最后一组的序号
- 属性方法
- span() 获取匹配内容的起止位置
- start() 获取匹配内容的开始位置
- end() 获取匹配内容的结束位置
- groupdict() 获取捕获组字典,组名为键,对应内容为值
- groups() 获取子组对应内容
- group(n=0)
功能:获取match对象匹配内容
参数:默认为0表示获取整个match对象内容,如果是序列号或者组名则表示获取对应子组内容
返回值:匹配的字符串
flags参数扩展
- 使用函数:re模块调用的匹配函数。如:re.compile, re.findall, re.search…
- 作用:扩展丰富正则表达式的匹配功能
- 常用flag
- A==ASCII 元字符只能匹配ascii码
- I==IGNORECASE 匹配忽略字母大小写
- S==DOTALL 使 . 可以匹配换行(\n)
- M==MULTILINE 使 ^ $ 可以匹配每一行的开头和结尾位置
- X==VERBOSE 为正则添加注释(当正则表达式中带注释,会把每行#后面的部分和#前面的空格当做注释)
pattern = r"""\w+ # 第一部分
\s+ # 第二部分
\w+ # 第三部分
"""
用法(以A为例):
e.g. regex = re.compile(r’\w+’, flags = re.A)
- 使用多个flag
方法:使用按位或连接(添加功能)
e.g. flags = re.I | re.A