Python正则表达式
字符串是编辑时涉及到最多的的一种数据结构,对字符串进行操作的需求几乎无处不在。那么有没有一种方法是专门用来匹配字符串的呢?正则表达式一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。
- re 模块使Python语言拥有全部的正则表达式功能。若依在使用正则表达式时,我们需要先导入re模块——import re;
- complie 函数更具一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。
一、基础知识
- 在正则表达式中,如果直接给出字符串,就是精确匹配。用\d 表示匹配一个数字,\w 表示可以匹配一个字母或数字;
- . 可以匹配任意字符;
- 要匹配变长的字符,在正则表达式中,用 + 表示至少一个字符,用 * 表示任意个字符(包括0个),用 ? 表示0个或1个字符串,用{n}表示n个字符串,用{n,m}表示n-m个字符。
import re #导库
re.match() #match函数从头开始匹配,如果不是起始位置匹配成功的话,match函数的匹配结果就为none
re.match() #搜素整个字符串,并返回第一个成功的匹配
re.search() #搜索整个字符串,返回一个list(最常用的)
re.findall() #compile 函数用于编译正则表达式,生成一个正则表达式(pattern)对象
re.split() #将一个字符串按照正则表达式匹配的结果进行分割,返回列表类型
re.sub() #在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串
示例:
import re #导库
astr="s34份额是否as586c河1源..is市9.1d3防4H不0防h7b不仅4.r5cd"
print(re.findall("\d",astr)) #findall()在字符串中找到所有的数字,并以列表的形式返回
print(re.search("\w",astr)) #匹配一个字母或数字,并将第一匹配到的结果返回
print(re.findall("\w\d",astr)) #匹配连续的两个数字,一个字母一个数字,一个汉字一个数字,并将所有匹配到的结果以列表的形式返回
print(re.findall(r"\d.\d",astr)) #匹配连续的三位——> 第一位和第三位都是数字,中间为任意字符
print(re.findall("\d+",astr)) #匹配1位到无穷多位连续的数字
print(re.findall("\d\d+",astr)) #匹配2位到无穷多位连续的数字
print(re.findall("\d\d*",astr)) #匹配1位到无穷多位的连续数字
print(re.findall("\d\d?",astr)) # ? 表示0或1位字符,故表示一位或两位连续数字
print(re.findall("\d{3}",astr)) #返回三位连续的数字
print(re.findall("\d{1,3}",astr)) #贪婪匹配,优先三位查询,接着两位,最后一位
print()re.findall("\d{2,3}?",astr) # ? 可将贪婪匹配变成非贪婪匹配;先匹配两位数字,然后再匹配三位数字
运行结果:
['3', '4', '5', '8', '6', '1', '9', '1', '3', '4', '0', '7', '4', '5']
<_sre.SRE_Match object; span=(0, 1), match='s'>
['s3', 's5', '86', '河1', '市9', 'd3', '防4', '不0', 'h7', '仅4', 'r5']
['586', '9.1', '3防4']
['34', '586', '1', '9', '1', '3', '4', '0', '7', '4', '5']
['34', '586']
['34', '586', '1', '9', '1', '3', '4', '0', '7', '4', '5']
['34', '58', '6', '1', '9', '1', '3', '4', '0', '7', '4', '5']
['586']
['34', '586', '1', '9', '1', '3', '4', '0', '7', '4', '5']
['34', '58']
二、强化阶段
要做更精确地匹配,可以使用[]表示范围,比如:
- [0-9a-zA-Z_] 可以匹配一个数字,字母或者下划线;
- [0-9a-zA-Z_]+ 可以匹配至少由一个数字,字母或者下划线组成的字符串,比如’a100’,‘0_Z’,'py3000’等等;
- [a-zA-Z_][0-9a-zA-Z_]* 可以匹配由字母或下划线开头,后接任意个有数字、字母或者下划线组成的字符串,也就是python合法的变量;
- [a-zA-Z_][0-9a-zA-Z_]{0,19} 精准地限制了变量的长度是1-20个字符(前面1个字符+后面(0-19)个字符)。
import re
astr="A_c8fd33jd9_k0ja3"
print(re.findall("[0-9a-zA-Z\_]",astr))
print(re.findall("[0-9a-zA-Z\_]+",astr))
print(re.findall("[a-zA-Z\_][0-9a-zA-Z\_]*",astr))
print(re.findall("[a-zA-Z\_][0-9a-zA-Z\_]{0,19}",astr))
运行结果:
['A', '_', 'c', '8', 'f', 'd', '3', '3', 'j', 'd', '9', '_', 'k', '0', 'j', 'a', '3']
['A_c8fd33jd9_k0ja3'] #贪婪匹配
['A_c8fd33jd9_k0ja3']
['A_c8fd33jd9_k0ja3']
- A|B 可以匹配A或B,所以’(P|p)ython’或者’python’。
- ^ 表示行的开头,^\d 表示必须以数字开头。
- $ 表示行的结束,\d$ 表示必须以数字结束。
- 你可能注意到了,py也可以匹配‘python’,但是加上^py$就变成了整行匹配,就可能匹配‘py’了。
- Python中字符串前面加上 r 表示原生字符串
import re
astr=astr="""csd3份额是否a9
sjhbh353758cdbsv河
1源..is市....cd3防4H不胜
防hh787bb不仅4.r5cd是is范德萨;‘’a8"""
print(re.findall(r"^\d",astr)) #换行符号不可见,将整段字符串按已行处理;行的开头为数字,则返回该数字,如不是,返回一个空列表
print(re.findall(r"^\d",astr,re.M)) #re.M 是换行符可见,将astr字符串转换为4行;行的结尾是数字,则返回该数字
print(re.findall(r"\d$",astr)) #行的结尾为数字
print(re.findall(r"\d$",astr,re.M)) #re.M 是换行符可见,将astr字符串转换为4行;行的结尾为数字,则以列表的形式返还该数字
运行结果:
[]
['1']
['8']
['9', '8']
三、正则表达式的具体用法
1、查询
(1)re.search函数
re.match 从字符串的起始位置匹配一个模式,如果起始位置匹配成功 re.match()方法返回一个匹配对象;其实位置没有匹配成功的话,match()就返回None。
语法如下:re.match(pattern,string,flags=0)
(1) pattern:需要匹配的正则表达式; (2)string:在那个字符串中就行行匹配; (3)flags:标志位(默认为0),它可以控制正则表达式的匹配方式;
常见的flags如下:
(1)re,l 忽略匹配时的大小写 (2)re.M 多行匹配,影响 ^ 和 $ (3)re.S. 默认不匹配换行,使 . 匹配包括换行在内的所有字符 (4)re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B
示例如下:
我们可以看出,match函数匹配成功的话,re.match方法返回一个匹配的对象,而不是匹配的正则表达式;通过span()可以获取匹配的位置。
>>> import re
>>> astr='11you are 3344 my apple\n 11开心果,you\n66a77'
>>> re.match('11',astr)
<_sre.SRE_Match object; span=(0, 2), match='11'>
>>> re.match('11',astr).span()
(0, 2)
>>> print(re.match('you',astr))
None
如果需要将匹配的正则表达式显示出来,我们就需要使用group(num)或group()匹配对象函数来获取匹配的表达式。
例如:re.match(r’\d(.)(\d)’,astr),在需要匹配的字符串中,可以有多个括号,每个括号为一组。
- group(0) 匹配的是整个表达式的字符串,即,\d(.)(\d);
- group(1) 表示第一个括号里的内容,即 (.);以此类推;
- group(num=2,3,4…) 表示对面括号里的内容;
- group() 返回一个包含所有括号里面的字符串的内容,返回的结果为元组;
>>> import re
>>> astr='11you are 3344 my apple\n 11开心果,you\n66a77'
>>> re.match('\d(\d)(.)',astr,re.S).group(0)
'11y'
>>> re.match('\d(\d)(.)',astr,re.S).group(1)
'1'
>>> re.match('\d(\d)(.)',astr,re.S).group(2)
'y'
>>> re.match('\d(\d)(.)',astr,re.S).groups()
('1', 'y')
(2)re.search函数
搜索整个字符串,并返回第一个成功的匹配。
语法如下:re.search(pattern, string, flags=0)
(1)pattern:需要匹配的正则表达式;
(2)string:需要匹配的字符串;
(3)flags:标志位(默认为0),它可以控制正则表达式的匹配方式;常见的flags如下:
(1)re.l 忽略匹配时的大小;
(2)re.M 多行匹配,影响 ^ 和 $
(3)re.S 默认不匹配换行,使 . 匹配包括换行在内的所有字符;
(4)re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B
示例如下:
我们可以看到,search函数匹配成功的话,re.search方法返回一个匹配的对象,而不是匹配的正则表达式;通过span() 可以获取匹配的位置。如果span()可以获取匹配的位置。如果没有匹配到,则返回为None。
>>> import re
>>> astr='11you are 3344 my apple\n 11开心果,you\n66a77'
>>> re.search('11',astr)
<_sre.SRE_Match object; span=(0, 2), match='11'>
>>> re.search('you',astr)
<_sre.SRE_Match object; span=(2, 5), match='you'>
>>> re.search('you',astr).span() #通过span()获取匹配的位置
(2, 5)
>>> re.search('11',astr).span()
(0, 2)
>>> print(re.search('22',astr))
None
如果需要将匹配的正则表达式显示出来,我们就需要使用group(num)或groups()匹配对象函数来获取匹配的表达式。
例如:re.search(r’\d(.)(\d)’,astr),在需要匹配字符串中,可以有多个括号,每个括号为一组。
- group(0) 匹配的是整个表达式的字符串,即,\d(.)(\d)
- group(1) 表示第一个括号里的内容,即 (.) ; 以此类推
- group(num=2,3,4…)表示对应括号的内容;
- groups() 返回一个包含所有括号里面的字符串的内容,返回的结果为一个元组。
>>> import re
>>> astr='1you are 3344 my apple\n 11开心果,you\n66a77'
>>> re.search('\d(\d)(.)',astr,re.S).group(0)
'334'
>>> re.search('\d(\d)(.)',astr,re.S).group(1)
'3'
>>> re.search('\d(\d)(.)',astr,re.S).group(2)
'4'
>>> re.search('\d(\d)(.)',astr,re.S).groups()
('3', '4')
re.match 函数与re.search函数的区别:
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配,如果没有匹配到,则返回None。
注意:match和search是匹配以此,而findall匹配所有。
(3) re.findall函数
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表;如果没有找到匹配的,则返回空列表。
语法如下:re.findall (pattern, string, flags=0)
示例如下:
>>> import re
>>> astr='1you are 3344 my apple\n 11开心果,you\n66a77'
>>> re.findall('\d\d',astr) #列表形式显示所有的两个数字
['33', '44', '11', '66', '77']
>>> re.findall('\d{2,4}',astr) #列表形式显示所有的2——4个数字,默认贪婪匹配
['3344', '11', '66', '77']
>>> re.findall('\d+',astr) #(1,无穷)
['1', '3344', '11', '66', '77']
>>> re.findall('\d*',astr) #(0,无穷)
['1', '', '', '', '', '', '', '', '', '3344', '', '', '', '', '', '', '', '', '', '', '', '11', '', '', '', '', '', '', '', '', '66', '', '77', '']
>>> re.findall('\d?',astr) #匹配0或1
['1', '', '', '', '', '', '', '', '', '3', '3', '4', '4', '', '', '', '', '', '', '', '', '', '', '', '1', '1', '', '', '', '', '', '', '', '', '6', '6', '', '7', '7', '']
>>> re.findall('\d{2,3}?',astr) #一个模式后跟?,不贪婪匹配,范围后面?,有两次就先取两次
['33', '44', '11', '66', '77']
>>> re.findall('\d.\d',astr) #匹配两个数字与中间任意字符
['334', '6a7']
>>> re.findall('^\d',astr) #以数字开头
['1']
>>> re.findall('^\d',astr,re.M) #多行匹配
['1', '6']
>>> re.findall('\d$',astr) #以数字结尾
['7']
>>> re.findall('\d$',astr,re.M) #多行匹配,影响^和$
['7']
>>> re.findall('\d(.)(\d)',astr,re.S)#列表形式返回,每项为一个元组
[('3', '4'), ('a', '7')]
(4)re.complie函数
complie函数用于编译正则表达式,生成一个正则表达式(Pattern)对象。
语法如下:re.compile(pattern,flags=0)
pattern:需要匹配的正则表达式;
flags:标志位(默认为0),它可以控制正则表达式的匹配方式这里是引用
常见的flags如下:
- re.I 忽略匹配时的大小写
- re.M 多行匹配,影响 ^ 和 $
- re.S . 默认不匹配换行,使 . 匹配包括换行在内的所有字符
- re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B
示例如下:
>>> import re
>>> astr='AS12as34er567q!"3456'
>>> m1=re.compile(r'\d\d') #编译
>>> m1.search(astr).group() #匹配
'12'
>>> m1.findall(astr)
['12', '34', '56', '34', '56']
>>> m2=re.compile(r'a',re.I) #编译
>>> m2.findall(astr) #匹配
['A', 'a']
(5)re.split函数
将一个字符串按照正则表达式匹配的结果进行分割,返回列表类型
语法如下:re.split(pattern, string, maxsplit=0,flags=0)
- pattern:需要匹配的正则表达式;
- string:在那个字符串中就行匹配;
- maxsplit:分割次数,maxsplit=1分割一次,默认为0,不限制次数。
- flags:标志位(默认为0),它可以控制正则表达式的匹配方式
常见的flags如下:
- re.l 忽略匹配时的大小写
- re.M 多行匹配,影响 ^ 和 $
- re.S. 默认不匹配换行,使 . 匹配包括换行在内的所有字符
- re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B
>>> import re
>>> astr='AS12as34er567q!"3456'
>>> astr.split('12') #通过12进行分割
['AS', 'as34er567q!"3456']
>>> re.split("\d{2}",astr) #通过两个数字进行分割
['AS', 'as', 'er', '7q!"', '', '']
>>> re.split("\d+",astr) #通过数字进行分割
['AS', 'as', 'er', 'q!"', '']
>>> m3=re.compile(r'\d+') #与上面等价,运用了compile函数
>>> m3.split(astr)
['AS', 'as', 'er', 'q!"', '']
>>> m3.split(astr,3) #指定分割几次
['AS', 'as', 'er', 'q!"3456']
(6). re.sub函数
在一个字符串中替换所有匹配正则表达式的字串,返回替换后的字符串
语法如下:re.sub(pattern, repl, string, count=0, flags=0)
- pattern: 需要匹配的正则表达式;
- repl: 替换的字符串,也可以是个函数;
- string: 在这个字符串中就行匹配;
- count: 模式匹配后替换的最大次数,默认0表示替换所有的匹配;
- flags: 标志位(默认为0),它可以控制正则表达式的匹配方式;
常见的flags如下:
- re.l 忽略匹配时的大小写;
- re.M 多行匹配,影响 ^ 和 $
- re.S. 默认不匹配换行,使 . 匹配包括换行在内的所有字符
- re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B
示例如下:
>>> import re
>>> astr='AS12as34er567q!"3456'
>>> re.sub("5",'9',astr) #将5替换为9
'AS12as34er967q!"3496'
>>> m4=re.compile(r"\d+")
>>> m4.sub(' ',astr) #将数字替换为空字符串
'AS as er q!" '
>>> m4.sub(' ',astr,2) #指定替换几次
'AS as er567q!"3456'
repl参数是一个函数,实现将字符串中的数字乘以2;
>>> import re
>>> def f(m):
... return str(2*int(m.group()))
...
>>> re.sub('\d',f,'a2233q')
'a4466q'
四、补充:
1、正则表达式修饰符——可选标志
正则表达式可以包括一些可选标志符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位OR(|)它们来指定。如re.l | re.M 被设置为 l 和 M标志:
修饰符 | 描述 |
---|---|
re.l | 使匹配对大小写不敏感 |
re.L | 做本地化识别(local-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志位影响 \w, \W, \b, \B |
re.X | 该标志位通过给予你更灵活的格式以便你将正则表达式写得易于理解。 |
2、正则表达式的常见模式
常见模式
模式 | 描述 |
---|---|
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾 |
. | 匹配任意字符,除换行符,当re.S标记被指定时,则可以匹配包括换行符在内的任意字符串 |
* | 前一个字符0次或无限次扩展 |
+ | 前一个字符1次或无限次扩展 |
? | 前一个字符0次或1次扩展,非贪婪方式 |
{m} | 扩展前一个字符m次。例如,ab{2}c表示abbc |
{m,n} | 扩展前一个字符m至n次(包含n次),贪婪匹配 |
a | b | 匹配a或b |
() | 分组标记,内部职能使用 | 操作符 |
\w | 匹配字母数字以及下划线,等价于[A-Za-b0-9_] |
\W | 匹配非字母数字以及下划线,等价于[^A-Za-z0-9_] |
\s | 匹配任意空白字符,等价于[\t\n\r\f] |
\S | 匹配任意非空字符,等价于[^\f\n\r\t\v] |
\d | 匹配任意数字,等价于[0-9] |
\b | 匹配一个单词边界,也就是指单词和空格间 的位置。例如,‘er\b’可以匹配“never”中的‘er’,但不能匹配“verb”中的‘er’。 |
\B | 匹配非单词边界。‘er\B’能匹配“verb”中的‘er’,但不能匹配“never”中的‘er’ |
3、正则表达式实例
实例 | 描述 |
---|---|
^ [A-Za-z]+$ | 由26个字母组成的字符串 |
^ [A-Za-z0-9]+$ | 由26个字母和数字组成的字符串 |
^-?\d+$ | 整数形式的字符串 |
[1-9]\d{5} | 中国境内邮政编码,6位 |
\u4e00-\u9fa5 | 匹配中文字符 |
\d{3}-\d{8}|\d{4}-\d{7} | 电话号码 |