目录
一、介绍
1、概述
正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。
正则表达式是一个很强大的字符串处理工具,几乎任何关于字符串的操作都可以使用正则表达式来完成。
在 Python 中需要通过正则表达式对字符串进行匹配的时候,可以使用 re 模块。
2、应用场景
表单验证(例如 : 手机号、邮箱、身份证… )
爬虫
处理文本和数据
3、格式
# 导入 re 模块
import re
# 使用 match 方法进行匹配操作 re.match() 能够匹配出以 xxx 开头的字符串
# 如果起始位置没有匹配成功,返回 None
result = re.match(正则表达式,要匹配的字符串)
# 如果上一步匹配到数据的话,可以使用 group 方法来提取数据
result.group()
4、案例
import re
result = re.match('it', 'itsixstar.cn')
print(result.group()) # it
二、匹配单个字符
字符 | 功能 |
---|---|
. | 匹配任意1个字符(除了\n) |
[ ] | 匹配[ ]中列举的字符 |
\d | 匹配数字,即0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即空格,tab键 |
\S | 匹配非空白 |
\w | 匹配单词字符,即a-z、A-Z、0-9、_ |
\W | 匹配非单词字符 |
import re
# . 匹配任意1个字符(除了\n)
ret = re.match(".","M")
print(ret.group()) # M
ret = re.match("t.o","too")
print(ret.group()) # too
ret = re.match("t.o","two")
print(ret.group()) # two
# [] 匹配[ ]中列举的字符
# 如果 hello 的首字符小写,那么正则表达式需要小写的 h
ret = re.match("h","hello Python")
print(ret.group()) # h
# 如果 hello 的首字符大写,那么正则表达式需要大写的 H
ret = re.match("H","Hello Python")
print(ret.group()) # H
# 大小写h都可以的情况
ret = re.match("[hH]","hello Python")
print(ret.group()) # h
ret = re.match("[hH]","Hello Python")
print(ret.group()) # H
ret = re.match("[hH]ello Python","Hello Python")
print(ret.group()) # Hello Python
# 匹配0到9第一种写法
ret = re.match("[0123456789]Hello Python","7Hello Python")
print(ret.group()) # 7Hello Python
# 匹配0到9第二种写法
ret = re.match("[0-9]Hello Python","7Hello Python")
print(ret.group()) # 7Hello Python
ret = re.match("[0-35-9]Hello Python","7Hello Python")
print(ret.group()) # 7Hello Python
# 下面这个正则不能够匹配到数字4,因此 ret 为 None
ret = re.match("[0-35-9]Hello Python","4Hello Python")
# print(ret.group()) # AttributeError: 'NoneType' object has no attribute 'group'
# 普通的匹配方式 \d 匹配数字,即0-9
ret = re.match("嫦娥1号","嫦娥1号发射成功")
print(ret.group()) # 嫦娥1号
# 使用\d进行匹配
ret = re.match("嫦娥\d号","嫦娥1号发射成功")
print(ret.group()) # 嫦娥1号
# \w匹配单词字符,即a-z、A-Z、0-9、_
res = re.match('\w', '4sixstar')
print(res.group()) # 4
三、匹配多个字符
字符 | 功能 |
---|---|
* | 匹配前一个字符出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即至少有1次 |
? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,n} | 匹配前一个字符出现从m到n次 |
import re
# * 匹配出,一个字符串第一个字母为大小字符,后面都是小写字母并且这些小写字母可有可无
ret = re.match("[A-Z][a-z]*","M")
print(ret.group()) # M
res = re.match('[A-Z]*[a-z]', 'SSixstar')
print(res.group()) # SSi
ret = re.match("[A-Z][a-z]*","Aabcdef")
print(ret.group()) # Aabcdef
# +:匹配出,变量名是否有效
ns = ['jiuge', '2jiuge', 'jg', '_jiuge']
for i in ns:
res = re.match('[ a-zA-Z_]+[\w]*', i)
if res:
print('正确的名字:', res.group())
else:
print("名字 %s 非法" % i)
# 运行结果:
# 正确的名字: jiuge
# 名字 2jiuge 非法
# 正确的名字: jg
# 正确的名字: _jiuge
# ? 匹配数字 匹配前一个字符出现1次或者0次,即要么有1次,要么没有
res = re.match('[1-9]?[0-9]', '6946')
print(res.group()) # 69
res = re.match('[1-9]?', '6946')
print(res.group()) # 6
res = re.match('[1-9]?\d', '78')
print(res.group()) # 78
res = re.match('[1-9]?\d', '078')
print(res.group()) # 0
# {m} 匹配前一个字符出现m次, 8到20位的密码,可以是大小写英文字母、数字、下划线
ret = re.match("[a-zA-Z0-9_]{6}","12a3g45678")
print(ret.group()) # 12a3g4
res = re.match('[a-z0-9]{4}', 'abc234dss')
print(res.group()) # abc2
ret = re.match("[a-zA-Z0-9_]{8,20}","1ad12f23s34455ff66")
print(ret.group()) # 1ad12f23s34455ff66
ret = re.match("[a-zA-Z0-9_]{3,8}", "abc")
print(ret.group()) # abc
四、匹配开头结尾
字符 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
import re
res = re.match('^ab', 'absdf') # 由'ab'开头就匹配成功
print(res.group()) # ab
res = re.match('^[0-9]', '23absdf') # ^[0-9]表示由0-9之间开头就成功
print(res.group()) # 2
res = re.match('[^0-9]', 'absdf') # [^0-9] 表示匹配所有的非数字字符
print(res.group()) # a
总结:
'abc'
表示字符串中有’abc’就匹配成功 。
'[abc]'
表示字符串中有’a’或’b’或’c’就匹配成功 。
'^abc'
表示字符串由’abc’开头就匹配成功 。
'^[abc]'
表示字符串由’a’或’b’或’c’开头就匹配成功 。
'[^abc]'
表示匹配’a’,‘b’,‘c’之外的字符。如果一个字符串是由’a’,‘b’,'c’组合起来的,那就是假。
import re
elist = ['jiuge@qq.com', 'jiu@qq.compu', 'com.jiuge@qq.com', 'jiuge@163.com']
for i in elist:
# res = re.match('[\w]*@qq.com$', i)
res = re.match('[\w]{4,20}@qq.com$', i)
if res:
print('%s 符合要求' % res.group())
else:
print('%s 不符合要求' % i)
# 运行结果:
# jiuge@qq.com 符合要求
# jiu@qq.compu 不符合要求
# com.jiuge@qq.com 不符合要求
# jiuge@163.com 不符合要求
五、匹配分组
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
(ab) | 将括号中字符作为一个分组 |
\num | 引用分组 num 匹配到的字符串 |
(?P) | 分组起别名 |
(?P=name) | 引用别名为 name 分组匹配到的字符串 |
import re
# | 匹配左右任意一个表达式
res = re.match('[1-9]?\d$', '10')
print(res.group()) # 10
res = re.match('[1-9]?\d$', '100')
# print(res.group()) # 报错
res = re.match('[1-9]?\d$|100', '100')
print(res.group()) # 100
# () 将括号中字符作为一个分组
res = re.match('\w{4,20}@163.com', 'test@163.com')
print(res.group()) # test@163.com
res = re.match('\w{4,20}@(163|qq|129).com', 'test@qq.com')
print(res.group()) # test@qq.com
import re
res = re.match("<[a-zA-Z]*>", "<html>hh</html>")
print(res.group()) # <html>
res = re.match("<[a-zA-Z]*>\w*", "<html>hh</html>")
print(res.group()) # <html>hh
res = re.match("<[a-zA-Z]*>\w*</[a-zA-Z]*>", "<html>hh</html>")
print(res.group()) # <html>hh</html>
ret = re.match(r"<(\w*)>\w*</\1>", "<html>hh</html>")
print(ret.group()) # <html>hh</html>
import re
ret = re.match(r"<(\w*)>\w*</\1>", "<html>hh</htmlre>")
# 因为2对<>中的数据不一致,所以没有匹配出来,前后不匹配,出现错误。
# print(ret.group()) # 报错:AttributeError: 'NoneType' object has no attribute 'group'
# \number
ret = re.match(r"<(\w*)><(\w*)>.*</\2></\1>", "<html><h1>hh</h1></html>")
print(ret.group()) # <html><h1>hh</h1></html>
ret = re.match(r"<(\w*)><(\w*)>.*</\2></\1>", "<html><h2>hh</h1></html>")
# print(ret.group()) # 报错:AttributeError: 'NoneType' object has no attribute 'group'
# (?P<name>) 分组起别名
ret = re.match(r"<(?P<n1>\w*)><(?P<n2>\w*)>.*</(?P=n2)></(?P=n1)>", "<html><h1>www.baidu.com</h1></html>")
print(ret.group()) # <html><h1>www.baidu.com</h1></html>
六、高级用法
1、search 函数
search() 会扫描整个字符串并返回第一个成功的匹配。
import re
res = re.search('\d+', '阅读次数为23次')
print(res.group()) # 23
search 函数和 match 函数有点类似,都可以匹配模式。
match 和 search 的区别:
match 函数只能够字符串的开始位置开始匹配,而 search 扫描整个 string 查找匹配,可以匹配字符串的任意位置,但也是返回找到的第一个匹配的模式。
import re
res = re.match('ab', '12abwe')
# print(res.group()) # 错误:AttributeError: 'NoneType' object has no attribute 'group'
res2 = re.search('ab', '12abwe')
print(res2.group()) # ab
2、findall
findall() 以列表形式返回匹配到的字符串。
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
match 和 search 是匹配一次,findall 匹配所有。
import re
res = re.findall('\d', 'a=1,b=2,c=3')
print(res) # ['1', '2', '3']
总结:
match:从头开始匹配,返回 object,匹配一次,最不常用。
search:仅返回第一个匹配,返回object,匹配一次,较常用 。
findall:匹配所有字符串,返回列表,匹配所有,最常用。
3、sub
sub() 将匹配到的数据进行替换。
re.sub(pattern, repl, string, count = 8, flags = 0)
其中必须的三个参数:pattern, repl, string
两个可选参数: count, flags
count 指定要替换的个数
import re
res = re.search('\d+', '20')
print(res.group()) # 20
res2 = re.sub('\d', '20', '我读了2页')
print(res2) # 我读了20页
import re
str1 = '''
<div>
<p>岗位职责:</p>
<p>完成推荐算法、数据统计、接口、后台等服务器端相关工作</p>
<p><br></p>
<p>必备要求:</p>
<p>良好的自我驱动力和职业素养,工作积极主动、结果导向</p>
<p> <br></p>
<p>技术要求:</p>
<p>1、一年以上 Python 开发经验,掌握面向对象分析和设计,了解设计模式</p>
<p>2、掌握HTTP协议,熟悉MVC、MVVM等概念以及相关WEB开发框架</p>
<p>3、掌握关系数据库开发设计,掌握 SQL,熟练使用 MySQL/PostgreSQL 中的一种<br></p>
<p>4、掌握NoSQL、MQ,熟练使用对应技术解决方案</p>
<p>5、熟悉 Javascript/CSS/HTML5,JQuery、React、Vue.js</p>
<p> <br></p>
<p>加分项:</p>
<p>大数据,数理统计,机器学习,sklearn,高性能,大并发。</p>
</div>
'''
b = re.sub(r'<[^>]*>| |\n', "", str1)
print(b)
# 运行结果:
# 岗位职责:完成推荐算法、数据统计、接口、后台等服务器端相关工作必备要求:良好的自我驱动力和职业素养,工作积极主动、结果导向技术要求:1、一年以上 Python 开发经验,掌握面向对象分析和设计,了解设计模式2、掌握HTTP协议,熟悉MVC、MVVM等概念以及相关WEB开发框架3、掌握关系数据库开发设计,掌握 SQL,熟练使用 MySQL/PostgreSQL 中的一种4、掌握NoSQL、MQ,熟练使用对应技术解决方案5、熟悉 Javascript/CSS/HTML5,JQuery、React、Vue.js加分项:大数据,数理统计,机器学习,sklearn,高性能,大并发。
4、split
split() 根据匹配进行切割字符串,并返回一个列表。
re.split(pattern, string, maxsplit=0, flags=0)
pattern compile 生成的正则表达式对象,或者自定义也可。
string 要匹配的字符串。
maxsplit 指定最大分割次数,不指定将全部分割。
import re
res = re.split(';| ', 'a b;c 23')
print(res) # ['a', 'b', 'c', '23']
七、贪婪和非贪婪
正则在进行匹配时,从开始位置查找最远的结束位置,这种模式称之为贪婪模式。
在进行 HTML 标签类似内容获取时,贪婪模式会导致整个内容的返回,需要使用非贪婪模式。
贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配。
非贪婪匹配:在满足匹配时,匹配尽可能短的字符串,使用 ? 来表示非贪婪匹配。
固定的书写规则 :
.*?
这种方式就是非贪婪模式,或者说是惰性模式。
非贪婪则相反,总是尝试匹配尽可能少的字符。
在"*“,”?“,”+“,”{m,n}"后面加上?
,使贪婪变成非贪婪。
import re
res = re.match('ab*', 'abbbc')
print(res.group()) # abbb
res2 = re.search('ab*?', 'abbbc')
print(res2.group()) # a
res = re.match('a*', 'aaa')
print(res.group()) # aaa
res2 = re.match('a*?', 'aaa')
res2 = re.match('a+?', 'aaa') # a
print(res2.group()) # 空字符串
import re
str3 = '<h1> hello world </h1>'
res = re.findall(r'<.*>', str3) # 贪婪模式
res2 = re.findall(r'<.*?>', str3) # 非贪婪模式
# 默认匹配到所有内容
print(res) # ['<h1> hello world </h1>']
# 只想匹配两个标签的内容
print(res2) # ['<h1>', '</h1>']
import re
st = 'ab1234ba'
res = re.match('ab(\d+)', st)
res2 = re.match('ab(\d+?)', st)
res3 = re.match('ab(\d+)ba', st)
res4 = re.match('ab(\d+?)ba', st)
print(res.group()) # ab1234
print(res2.group()) # ab1
print(res3.group()) # ab1234ba
print(res4.group()) # ab1234ba
八、原生字符串
1、Python 中字符串前面加上 r 表示原生字符串。
2、python 转义字符
\b
退格(Backspace)
\e
转义
\n
换行
\t
横向制表符
\'
单引号
print('abc\n123')
print('abc\\n123')
# 运行结果:
# abc
# 123
# abc\n123
3、假如你需要匹配文本中的字符 \
,那么使用编程语言表示的正则表达式里将需要4个反斜杠\\\\
。
前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。
因为在正则表达式中,\\
就是一个反斜杠。
import re
res = re.match('\\\\', '\def')
print(res.group()) # \
4、正则表达式可以使用 r'\\'
表示。
import re
res = re.match(r'\\', '\def')
print(res.group()) # \
m = 'c:\\a\\b\\d'
print(m) # c:\a\b\d
res = re.match('c:\\\\', m)
print(res.group()) # c:\
res2 = re.match(r'c:\\a', m)
print(res2.group()) # c:\a
记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~