本文代码基于Python3.11解释器,除了第一次示例,代码将省略 import re
这个语句
所有示例代码均可以在我的github仓库中的 code.py文件内查看
搞清楚Python正则表达式语法,这一篇就够了
1.Python正则表达式匹配文本模式方法
正则表达式是一种快速从文本中匹配对应模式文本的表达式,在Python中所有的正则表达式函数都在模块re中。
import re
mo1 = re.compile('Batman' # 先使用re的方法compile,compile的字符串参数便是一个正则表达式
# compile讲返回一个一个Regex对象,mo1就是对应正则表达式模式的对象
name1 = mo1.search('My favorite hero is Batman' # 使用mo1对象中search方法,这个方法的字符串参数就是需要被查找的字符串
# 匹配成功,那么将返回一个Match对象给name1,这个对象中有group(方法,它返回与正则表达式匹配的字符串(有些情况不全是字符串,我会在后面作解释
# 匹配失败,返回None
print(name1.group(
输出结果如下:
2.直接查找模式
2.1直接查找
Batman',则需要在字符串'My favorite hero is Batman',寻找'Batman'
2.2管道匹配多种模式
|’这个符号来表示管道匹配,即匹配多种可能
mo2 = re.compile(r'Batman|Superman'
name2 = mo2.search('My favorite hero is Superman'
print(name2.group(
正则表达式是'Batman|Superman',那么在search(的字符串参数中,与之匹配的是'Superman',那么返回的Match对象赋给name2,其方法group(返回’Superman‘
2.3管道匹配多种分组模式
可以,但可以用简洁的形式'(Bat|Super|Spiderman'
mo3 = re.compile('(Bat|Super|Spiderman'
name3 = mo3.search('My favorite hero is Spiderman'
print(name3.group(
print(name3.group(0 # 0默认是整个匹配的字符串
print(name3.group(1 # 1是匹配的第一个分组
search(进行匹配时,先匹配第一个分组’Bat‘’Super‘’Spider‘中的一个,再匹配’man‘;也可以认为是匹配’Batman‘'Superman''Spiderman'中的一个。
需要指出的是,在正则表达式中出现分组时,可以在group中传入参数,参数作为索引,比如在上述代码的group(1,此方法将返回第一个分组,同时,无参数或者参数为0则默认返回整个匹配文本
search(只会返回含有第一个出现的匹配文本的对象
mo4 = re.compile('(Bat|Superman'
name4 = mo4.search('I love Superman and Batman'
print(name4.group( # 只输出第一个出现的Superman
运行结果如下:
Superman‘和'Batman'两个,但是name4对象的group方法只返回了第一个出现的'Superman'。
后面会有找到所有匹配文本的方法
3.查找固定类型字符模式
3.1字符类型
缩写字符 | 匹配字符 |
---|---|
\d | 0~9的数字 |
\D | 除了0~9的其它字符 |
\w | 字母,数字,下划线 |
\W | 除了字符数字下划线 |
\s | 空格制表换行符 |
\S | 除了空格制表换行符 |
3.2固定类型模式
使用上面的缩写字符,可以匹配指定类型的字符
mo5 = re.compile(r'\d\d\d\d\d\d\d\d\d\d\d'
phone_number1 = mo5.search('我的电话号码是15600000000'
print(phone_number1.group(
比如,如果无r前缀,那么字符串中的'\n'将被解释为换行符,但是如果加上r前缀,那么会被解释为''和'n'两个字符。
mo = re.compile('\\d'
num = mo.search('abcd6ef'
print(num.group(
运行结果如下:
但是需要注意的是,以下几个在正则表达式中有特殊含义的字符即使前面有r,仍然要加转义字符''来匹配这些特殊字符
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
4.分组模式
有时我们需要给查找到的电话号码分段,比如前面的+86前缀,这时,就可以用到分组模式
mo5 = re.compile(r'(\+86(\d\d\d(\d\d\d\d(\d\d\d\d'
phone_number1 = mo5.search('我的电话号码是+8618900000000'
print('电话号码'+phone_number1.group(
print('前缀'+phone_number1.group(1
在书写正则表达式的时候,给前缀+86分了一组,其后按照我的阅读习惯,344数量格式再分了三组
字符串 | 组索引 |
---|---|
+86 | 1 |
189 | 2 |
0000 | 3 |
0000 | 4 |
运行结果是
5.可有可无的分组的模式
mo6 = re.compile(r'(\+86?(\d\d\d(\d\d\d\d(\d\d\d\d'
phone_number2 = mo6.search('他输入了+8615600000000到电话框'
phone_number3 = mo6.search('另一个人输入18900000000'
print(phone_number2.group(
print(phone_number3.group(
在这个正则表达式中,我们对(+86进行可选匹配,匹配结果有'+86',返回字符串会带'+86',反之不带
第二个文本匹配返回对象中会包含返回'18900000000'的方法
6.含有特殊字符的模式
mo7 = re.compile(r'(\(\+\d\d\(\d\d\d(\d\d\d\d(\d\d\d\d'
phone_number4 = mo7.search('我的电话号码是(+8615600000000'
print(phone_number4.group(
r'(\(\+\d\d(\d\d\d(\d\d\d\d(\d\d\d\d'中,前缀r表示后面的字符串是原始字符串
模式 | 组索引 |
---|---|
(+\d\d | 1 |
\d\d\d | 2 |
\d\d\d\d | 3 |
\d\d\d\d | 4 |
组1匹配小括号括起来的加号和两个数字
三个数字
四个数字
7.接收任意个数的字符的模式
0到多次,即在search(的文本中可以不出现也可以出现多次,示例代码如下:
mo8 = re.compile(r'\d*%'
money = mo8.search('本期涨幅有143%'
print(money.group(
money = mo8.search('本期涨幅为?%'
print(money.group(
在第一个正则表达式中,'\d*%'可以匹配'143%',其中'1' '4' '3'都与'\d'匹配
'\d*%'可以匹配'%',因为'%'前没有数字类型可以与'\d'匹配
8.接收一个存在的连续字符模式
1到多次,即在search(的文本中可以出现一次也可以出现多次,示例代码如下:
mo9 = re.compile(r'\d+'
numbers = mo9.search('第一产业增加值54779亿元'
print(numbers.group(
这个正则表达式中,可以匹配到'54779'这5个数字,其中每个数字型文本都与'\d'匹配
mo9 = re.compile(r'\d+亿元'
numbers = mo9.search('第一产业增加值????亿元'
print(type(numbers
这里如果我们直接输出numbers.group(返回字符串会报错。马上来解释,现在我们用type(函数测试一下变量numbers的数据类型
事实上,search(方法在找不到匹配文本时就会返回一个NoneType类型
*和+的区别
修饰字符 | 匹配文本出现次数 |
---|---|
* | 0到多次 |
+ | 1到多次 |
不用出现;
必须出现。
9.字符匹配固定次数
9.1固定次数
如果'{}'括号内只有一个整数,如'\d{3}',表示只匹配3个数字类型字符
mo10 = re.compile(r'(\+\d\d?(\d{11}'
phone_number5 = mo10.search('电话号码是15600000000'
print(phone_number5.group(
这段代码中,正则表达式r'(\+\d\d?(\d{11}'表示'+\d\d'是可选匹配,可有可无,后面将匹配连续的11个数字类型字符,也就是说'15600000000'将匹配'\d'11次,运行结果如下:
我们试一试:
9.2次数范围
'{}'内可以用逗号把两个升序整数分开,比如'\d{11,13}',表示可以匹配11到13个数字字符
mo11 = re.compile(r'\+?\d{11,13}'
phone_number6 = mo11.search('电话号码是+8615600000000'
print(phone_number6.group(
正则表达式r'\+?\d{11,13}'表示'+'是可选匹配,而'\d{11,13}'将匹配11到13个数字类型字符,在被查找文本当中'+'匹配正则表达式的'\+?','8615600000000'匹配'\d{11,13}'
9.3贪心匹配与非贪心匹配
也就是说,正则表达式如果是'\d{4,6}?',那么,匹配结果是'1234',返回最少的字符。
# 贪心匹配方式
mo12 = re.compile(r'\d{3,5}'
num1 = mo12.search('数字有34567'
print(num1.group( # 匹配最多的数字
# 非贪心匹配
mo13 = re.compile(r'\d{3,5}?'
num2 = mo13.search('数字有34567'
print(num2.group( # 匹配最少的数字
运行结果:
9.4注意
2)'{}'允许'{3,3}'这样的写法,和'{3}'同义
*查找文本所有的匹配项findall(方法
上述用到的search(方法只能查找到第一个出现的匹配文本项,如何找到全部匹配项呢?
mo14 = re.compile(r'\d{11}'
phone_number7 = mo14.findall('电话号码1:15600000000'
'电话号码2:19100000000'
'电话号码3:18700000000'
print(phone_number7
我们想要在文本中找到所有和'\d{11}'能匹配的字符,'15600000000'等电话号码都可以和正则表达式匹配,findall(将返回一个包含这些匹配文本的字符串列表
findall(直接返回一个列表而不是Match对象,所以在上面千万别把print(phone_number7
写成print(phone_number7.group(
了。
这个返回结果不含有在正则表达式中未分组的部分
mo15 = re.compile(r'电话号码\d:(\d{3}(\d{4}(\d{4}'
phone_number8 = mo15.findall('电话号码1:15600000000'
'电话号码2:19100000000'
'电话号码3:18700000000'
print(phone_number8
这里,我们分别对电话号码 前三位,中间四位,最后四位 分组,那么单个匹配文本会被分成三个字符串,组成一个元组,而这些元组组合成一个列表
10.自定义匹配字符类型
10.1匹配指定字符
代码示例如下:
mo16 = re.compile(r'\b[aeiouAEIOU]\w*'
vowel_word = mo16.findall('I am obviously angry with you'
print(vowel_word
这里先介绍一下'\b'这个字符,这个字符将匹配单词的分界,也就是说将从一个单词开始匹配。
使用自定义匹配字符[aeiouAEIOU]匹配元音开头,'\w*'匹配除了空格,制表符,换行符外的字符。
10.2匹配指定的字符无需加\转义
但是,在指定字符匹配当中,无需加''转义,示例代码如下:
mo17 = re.compile(r'[*?+]+'
special_character = mo17.findall('*+?11*?'
print(special_character
在这个正则表达式中,特殊字符'*' '?' '+'前并未加''转义,将匹配连续的几个指定字符组成的字符串,被匹配文本中'*+?'和'*?'符合
10.3匹指定字符外的字符
代码如下:
mo18 = re.compile(r'\b[^aeiouAEIOU\n\t ]\w*'
non_vowel_word = mo18.findall('I am obviously angry with you'
print(non_vowel_word
在这个正则表达式中,'^'表示不匹配元音字符和换行符,制表符和' '空格符。也就是说,这个正则表达式匹配非元音字母开头单词
11.^和$在正则表达式中的作用
11.1^的作用
mo19 = re.compile(r'^(name:(\d+'
name_phone1 = mo19.search('name:15600000000这是信息的格式'
print(name_phone1.group(
name_phone1 = mo19.search('信息的格式是name:15600000000'
print(type(name_phone1
11.2$的作用
你知道我要说什么
mo20 = re.compile(r'(name:(\d+$'
name_phone2 = mo20.search('信息的格式是name:15600000000'
print(name_phone2.group(
name_phone2 = mo20.search('name:15600000000这是信息的格式'
print(type(name_phone2
与'^'相反,'$'字符将让正则表达式匹配字符串末尾的文本,比如上面的两个字符串,几乎可以直接推断出可以匹配的是第一个
12.通配字符‘.’,匹配除了换行符的所有字符
mo21 = re.compile(r'.at'
words = mo21.findall('The cat in the hat sat on the flat mat'
print(words
正则表达式将匹配末尾带有'at'的所有字符串,除了'at'位于一行开头这种情况,看看结果吧:
12.1 '.*'匹配所有的字符
'*'同样可以修饰'.',来达到匹配除了换行符外所有的字符的效果。
12.1.1贪心匹配(匹配最多的字符串)
mo22 = re.compile(r'names:(.* phone number:(.*'
users_info = mo22.findall('names:Mike phone number:15600000000 '
'names:Jack phone number:18100000000 '
'names:John phone number:16200000000 '
print(users_info
正则表达式将匹配'names:'开头加上其后的所有字符,直到遇到换行符或者字符串最后为止。
注意我这里的被查找字符串是由空格分开而非换行符,为方便看每行内容我用三个 '' 分开了字符串。
1)列表中只含一个元组,说明整个正则表达式只匹配了一个字符串
'names:'匹配了被查找字符串的'names:',正则表达式'(.'匹配了从'Mike'到'John'的所有字符,正则表达式' phone number:'匹配了被查找字符的'phone number:',第二个'(.'匹配了'16200000000'
12.1.2非贪心匹配(匹配最少的字符串)
mo23 = re.compile(r'names:(.*? phone number:(.*? '
users_info = mo23.findall('names:Mike phone number:15600000000 '
'names:Jack phone number:18100000000 '
'names:John phone number:16200000000 '
print(users_info
先来看看运行结果:
1第一组匹配:'names:'匹配'names:','(.*?'匹配'Mike',空格匹配空格,'phone number'匹配'phone number','(.*?'匹配'15600000000'
12.2 '.*'匹配换行符
事实上可以通过向compile(方法传关键字参数就可以让'.*'匹配换行符。
mo24 = re.compile('.*'
sentences = mo24.search("I can see empty streets.\nBut I can't sleep empty sheets\n"
print(sentences.group(
由于'.'不能匹配换行符,所以文本只能匹配到'I can see empty streets.'
现在我们将关键字实参re.DOTALL
传入compile(方法
mo25 = re.compile('.*', re.DOTALL
sentences = mo25.search("I can see empty streets.\nBut I can't sleep empty sheets\n"
print(repr(sentences.group(
print(sentences.group(
容我先解释一下 repr(str
函数的作用,它返回字符串str的原字符串,不进行转义。
'.*'将匹配所有的字符,那么,search(将要返回的是,包含所有被查找文本字符的对象
13.模糊大小写
我们在输入验证码验证自己不是机器人时,往往大小写均可以让自己通过。那么在正则表达式当中,我们也想在匹配时忽略大小写。只需要向compile(方法传入关键字实参re.IGNORECASE
即可,速记一下就是ignore case大写并且去空格
mo26 = re.compile(r'england|china|america', re.IGNORECASE
country_name = mo26.search("THE PEOPLE'S REPUBLIC OF CHINA"
print(country_name.group(
由于大小写模糊,'CHINA'和正则表达式中的'china'匹配
14.更好得书写管理正则表达式
14.1多段 '' 连接
比如我们想在一串文本中找出大陆的电话号码,使用 r''
分行并写下注释,那么可以通过以下示例代码查找:
mo27 = re.compile(r'(\+86?' # 大陆电话前缀
r'(\d\d\d' # 电话前三位
r'(\d\d\d\d' # 电话中间四位
r'(\d\d\d\d' # 电话后四位
phone_number9 = mo27.search('电话号码是:15600000000'
print(phone_number9.group(
运行结果:
14.2关键字实参re.VERBOSE
''' ''' 标识,来使正则表达式更易读。
mo28 = re.compile(r'''(\+86? # 大陆电话前缀
(\d{3} # 电话前三位
(\d{4} # 电话中间四位
(\d{4} # 电话后四位
''', re.VERBOSE
phone_number10 = mo28.search('电话号码是:15600000000'
print(phone_number10.group(
运行结果:
15.多个关键字实参
mo = re.compile(r'',re.DOTALL,re.IGNORECASE
compile(最多只有两个参数。
可以在几个关键字实参之间用'|'间隔达到多个关键字实参的效果
mo29 = re.compile(r'nice to meet you,.*',re.DOTALL|re.IGNORECASE
response = mo29.search('NICE to Meet You,Sir.\nHow can I help you?'
print(repr(response.group(
print(response.group(
这样,既可以有 模糊大小写的效果,也有让 '.'匹配换行符的效果
感谢你阅读我的博客,如果你对我的内容有任何的意见、建议或者问题,欢迎在评论区留言,我会尽快回复。如果你发现了我的错误或者疏漏,也请不吝指正,我会及时修改。希望我的博客能对你有所帮助,也期待与你的交流和分享。