Python中的正则表达式
一、 概述
1.1什么是正则表达式?
正则表达式(Regular Expression) 是一种文本模式,包括普通字符和特殊字符,特殊字符又称为元字符。代码中有regexp或regex或RE这几种说法。正则表达式用单个字符串,来描述或匹配某个字符串的句法规则。一般用来查找文本。
就是通过普通字符+特殊字符组成一个字符串,然后按照特定的语法描述一个规则,通过这个规则来查询对应的字符串。
1.2 正则表达式的基础语法
^ 代表字符串的开始位置
$ 匹配输入字符串的结束位置
+ 匹配一个或者多个,表示前面的字符至少出现一次(1次或者多次)
* 匹配的字符出现0次或者多次
? 前面的字符最多只能出现一次(0次或者1次)
1.3普通字符的匹配
二、 一些常用的规则的描述
2.1手机号
如 : 132-9527-2048,首先分析对象的描述和结构。
第一位:1 是固定的,都是1
第二位:只能是一些特定的数字 [34578]
第三位到第十一位共9位:可以为任意的数值
^1[34578][0-9]{9}$
图上所示,以1开始,[34578]中的任意一个,[0-9]中的任意数字重复9次(1个+8次重复),其中[0-9]也可以用\d 来代替,和转义字符类似,称之为元字符。
2.2文本的匹配
如有一个句子: “This is a book”,想要从中查询“is”这个单词。
\bis\b
可以通过notepad++来验证正则表达式
2.3 HTTP地址的匹配
一般的http的地址为: http://www.baidu.com
其中http://www.是固定的字符,结尾的.com也是固定,中间是任意字符。其中需要注意的就是元字符要用反斜杠转义。
http[s]?:\/\/www\.+.+\.com
如果有可能是https的化,那可以如下:
http[s]?😕/www.+.+.com
2.4 日期
日期的格式为:2019/1/17或者2019-01-17,可以使用如下的正则表达式:
[0-9]{4}[/-][0-9]{2}[/-][0-9]{2},要求月份和日期必须要是用两位数字来表示,其中[0-9]可以用定义好的\d来表示,所以为:
\d{4}[/-]\d{2}[/-]\d{2}
3times 由{4} 生效表示重复4次,开始的时候有疑问,为什么不是 4times 。后来明白作者的用意,正则表达式是一个规则,用这个规则去从字符串开始匹配到结束(注意计算机读字符串可是不会分行的,都是一个串,我们看到的多行,人家会认为是个 \t )这里设计好像小火车的轨道一直开到末尾。digit 传过一次,3times表示再来三次循环,共4次,后面的once同理。
可以对正则进行一个分组,分组的作用后面再补充:
(\d{4})/-/-
三、python中正则表达式的使用
python中存在re模块,具有所有正则表达式的所有功能。
3.1 python中正则表达式查询函数
3.1.1 re.match
语法规则:
re.match(pattern, string, flags=0)
match方法是从目标字符串的开头开始匹配一个模式,如果有内容但是不是起始位置匹配成功,就会返回none。
只能匹配开始是要匹配的字符,否者返回None,有点像正则中匹配开头的字符^
1、"2022年2月12日",此时,使用re.match()方法,可以正确匹配到20
2、"今天是2022年2月12日",此时用re.match()方法,无法匹配(必须是2022开头的字符串)
import re
s = 'Hey,he is a boy'
print('match:',re.match(r'\bis\b', 'He is a boy'))
print('match:',re.match(r'\bis\b', 'is a boy?'))
返回值:
match: None //虽然有匹配项,但是不是第一个返回None
match: <re.Match object; span=(0, 2), match='is'> //第一个,返回是所在的位置和长度,match返回匹配的内容
3.1.2 正则表达式中的分组和匹配
3.1.2.1 分组的语法
可以通过 re.match(pattern,string).group(num=0) 的方法来获取到各个分组,其中num为0-任意数字,为0的时候表示是所有。
3.1.2.2 分组的例子
如下的例子表示从日期中将分隔符号转为年月日,将2019-01-18转换成2019年01月18日。
import re
match_obj = re.match(r'(\d{4})[/-](\d{2})[/-](\d{2})','2019-01-18')
g0 = match_obj.group(0)
g1 = match_obj.group(1)
g2 = match_obj.group(2)
g3 = match_obj.group(3)
# g0表示全部,g1表示第一个分组,以此类推
print(f'g0:{g0}\ng1:{g1}\ng2:{g2}\ng3:{g3}')
s1 = '{}年{}月{}日'.format(g1,g2,g3)
print(s1)
# 使用groups返回tuple
print(f'groups:{match_obj.groups()}')
运行结果:
g0:2019-01-18
g1:2019
g2:01
g3:18
2019年01月18日
groups:('2019', '01', '18')
所以在正则表达式进行分组,是为了后面能够提取出各个分组中的内容,
这个例子看起来用字符串中的replace方法或者也合适,但是要在众多的字符串中查找到符合日期类型的字符串,还是用正则表达式更合适,如下用的是search()。
4545df45ads456f546as45f45as4d5fa546f45a54f54ad45fasdfa
fasd4f56a54sdf445asfadfjaiosdfioaijfjiasjidfijjifjaisjdfasdfaf54sad5f6a4dfa54sdfas65d54f4.>56asd564f54ad456ffjaodifajsdfoasdfasdf1as3df53as5df15as123f12312f123a213sdf132a2>13sdffasd4f56a54sdf445asfadfjaiosdfioaijfjiasjidfijjifjaisjdfasdfaf54sad5f6a4dfa54sdfas65>>d54f4>56asd564f54ad456ffjaodifajsdfoasdfasdf1as3df53as5df15as12asdfasdf3f12312f123>a213sdf132a213sdff45a654df5a6sdfadfjasidfaoojidfajidfijajidfadsfafad1121315412019->02-06545456456456asdjfiaiosdjfoiadsjifajisdjifoaad4fa65df4a4d
with open(r'd:\Python\Test1034.txt','r') as f_txt:
for f in f_txt:
res = re.search(r'(\d{4})[/-](\d{2})[/-](\d{2})',f)
if res:
print(res.group())
返回值:2019-02-07
3.1.2.3 使用groups()来返回所有的分组的元组
通过如下的例子可以看出使用group()和使用groups()的差别,group返回了整个匹配的字符串,而groups返回的是正则分组捕获的内容的tuple
match_obj = re.search(r'(\d{4})[/-](\d{2})[/-](\d{2})','Today is 2019/01/18,yesterday is 2019-01-17')
print(match_obj.groups())
print(match_obj.group())
返回值:
('2019', '01', '18')
2019/01/18
import re
with open(r'user_info_file.txt','r') as f:
str1 = str(f.readlines())
print(type(str1),str1)
res=re.search(r'(\d{4})\-\>(\d{2})\-(\d{2})',str1)
print(res.group())
print(res.group(1))
print(res.group(2))
print(res.group(3))
print(res.groups())
print(res.groups()[0])
print(res.groups()[1])
print(res.groups()[2])
运行的结果:
/Users/yangfan/Documents/pythonProject1/bin/python /Users/yangfan/Doc/pythonProject1/t2.py
<class 'str'> ['4545df45ads456f546as45f45as4d5fa546f45a54f54ad45fasdfafasd4f56a54sdf445asfadfjaiosdfio2022->07-02aijfjiasjidfijjifjaisjdfasdfaf54sad5f6a4dfa54sdfas65d54f4.>56asd564f54ad456ffjaodifajsdfoasdfasdf1as3df53as5df15as123f12312f123a213sdf132a2>13sdffasd4f56a54sdf445asfadfjaiosdfioaijfjiasjidfijjifjaisjdfasdfaf54sad5f6a4dfa54sdfas65>>d54f4>56asd564f54ad456ffjaodifajsdfoasdfasdf1as3df53as5df15as12asdfasdf3f12312f123>a213sdf132a213sdff45a654df5a6sdfadfjasidfaoojidfajidfijajidfadsfafad1121315412019->02-06545456456456asdjfiaiosdjfoiadsjifajisdjifoaad4fa65df4a4d']
2022->07-02
2022
07
02
('2022', '07', '02')
2022
07
02
3.1.1 re.search
和match函数类似,search所查找的内容是整个字符串的,并且返回第一个匹配的对象。
返回的是第一个匹配的对象,不是返回所有的匹配的对象。
match_obj = re.search(r'(\d{4})[/-](\d{2})[/-](\d{2})','Today is 2019/01/18,yesterday is 2019-01-17')
print(match_obj)
re.match和re.search的区别:re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
3.1.2python中正则表达式检索和替换
上面提供了正则表达式的查找和检索,后面提供的是替换。应该包括4个部分:正则表达式、要替换的字符串、替换后的内容、替换多少个,sub是substitute表示替换。
re.sub(pattern, repl, string, count=0, flags=0)
参数:
pattern : 正则中的模式字符串。
repl : 替换的字符串,也可为一个函数。
string : 要被查找替换的原始字符串。
count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。
sub_obj = re.sub(r'\btall\b', 'low','She is young,he is tall,tall and handsome',1)
print(sub_obj)
返回值:
She is young,he is low,tall and handsome
如果将其中的count改为2,那就会进行2次替换。
返回值:
She is young,he is low,low and handsome
例子2:
import re
s = '今天的天气是晴天,明天的天气也是晴天,后天的天气是多云转晴天'
# 使用re模块,替换s中的晴天的字符串,设置count=1
s1 = re.sub(pattern='晴天',repl='多云',string=s,count=1)
print('s1:',s1)
# 使用re模块,替换s中的晴天的字符串,设置count=1
s2 = re.sub('晴天','阴天',s,count=3)
print('s2:',s2)
3.1.2.1 参数中repl是一个函数的情况
例如将一个字符串中的所有的数字都翻倍:
def double_digtal(dig):
return str(int(dig.group())*2)
s = 'A564ASDF4ASD4F57835A4FA4DF5A54'
# 使用正则表达式将单个数字进行翻倍
sub_obj = re.sub('\\d',double_digtal,s)
print(sub_obj)
返回值:A10128ASDF8ASD8F101416610A8FA8DF10A108
延申的一个问题,将连续的数字进行翻倍,而不是对单个数字进行翻倍。
主要是正则表达式的写法问题,使用\d+开启贪婪,可以查找到尽量多的内容。
# 正则表达式开启贪婪模式
s = 'a460df4000000000005a1545455461d4a56fa1a23d'
lst_res = re.findall(r'\d+',s)
print(lst_res)
结果:
['460', '4000000000005', '1545455461', '4', '56', '1', '23']
# 正则表达式关闭贪婪模式
结果:
['4', '6', '0', '4', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '5', '1', '5', '4', '5', '4', '5', '5', '4', '6', '1', '4', '5', '6', '1', '2', '3']
如果使用?关闭贪婪模式,和使用\d的效果是一样的
例子:
import re
def dashrepl(matchobj):
print(f'matchobj:{matchobj}')
print(f'match.group(0):{matchobj.group(0)}')
if matchobj.group(0) == '-': return ' '
else: return '*'
res = re.sub('-{1,2}', dashrepl, 'pro----gram-files')
print(f'res:{res}')
# 运行的结果 强两次次匹配到--,然后替换成* ,最后一次匹配到-,替换成空格。
matchobj:<re.Match object; span=(3, 5), match='--'>
match.group(0):--
matchobj:<re.Match object; span=(5, 7), match='--'>
match.group(0):--
matchobj:<re.Match object; span=(11, 12), match='-'>
match.group(0):-
res:pro**gram files
3.1.2.2 re.compile
在match和search以及sub中,都会使用正则表达式,也可以将正则表达式事先编译好作为一个对象,然后再使用,这样的好书
语法:
re.compile(pattern[, flags])
pattern: 字符串形式的正则表达式
flages 字段的内容如下:
re.I 忽略大小写
re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
re.M 多行模式
re.S 即为 . 并且包括换行符在内的任意字符(. 不包括换行符)
re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库
re.X 为了增加可读性,忽略空格和 # 后面的注释
如在一个字符串中查找数字:
s = 'a46df45a151d4a56fa1a23d'
compile_sub = re.compile(r'\d')
# 这里等价于match.(r'\d',s)
res = compile_sub.match(s,1) # 返回的是match的对象
print(res)
print(res.group())
print(res.span())
返回值:
<re.Match object; span=(1, 2), match='4'>
4
(1, 2)
3.1.2.3 findall
在字符串中找到正则表达式匹配的所有的字符串,返回一个list,没有找到就返回空列表。和match和search的区别比较明显,match和search是匹配一次,而findall是匹配所有。
findall(string[, pos[, endpos]])
如果有需要,可以指定查找的开始和结束的位置
s = 'a46df45a151d4a56fa1a23d'
lst_res = re.findall(r'\d',s)
print(lst_res)
结果:['4', '6', '4', '5', '1', '5', '1', '4', '5', '6', '1', '2', '3']
达到了找出字符串中所有数字的目的
当然也可以通过string的方法进行数字的查找
list_res = []
for i in s:
if i.isdigit():
list_res.append(i)
print(list_res)
返回值:['4', '6', '4', '5', '1', '5', '1', '4', '5', '6', '1', '2', '3']
3.1.2.4 finditer 查找所有匹配,以迭代器返回
用法和findall相同,返回的是match对象的迭代器(地址),而更不是地址,这样在大量返回值的时候会更加节约内存开销。
re.finditer(pattern, string, flags=0)
s = 'a46df45a151d4a56fa1a23d'
iter_f = re.finditer(r'\d',s)
print(iter_f.__next__()) //迭代器的__next__()方法
3.1.2.5 split
将字符串分隔后,返回列表
re.split(pattern, string[, maxsplit=0, flags=0])
pattern 匹配的正则表达式
string 要匹配的字符串。
maxsplit 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。>参见:正则表达式修饰符 - 可选标志
例子:使用成绩这个字符串进行分割。
import re
s='134语文成绩148数学成绩139英语成绩200其他成绩'
res = re.split('成绩',s)
print(res)
['134语文', '148数学', '139英语', '200其他', '']
浙公网安备 33010602011771号