正则表达式
正则表达式具有伟大技术发明的一切特点,它简单、优美、功能强大、妙用无穷。对于很多实际工作来讲,正则表达式简直是灵丹妙药,能够成百倍地提高开发效率和程序质量。
正则表达式很有用,但如果不是程序员,很少会有人了解它,尽管大多数现代文本编辑器和文字处理器,都有查找和查找替换功能,可以根据正则表达式查找。正则表达式可以节约大量时间,不仅适用于软件用户,也适用于程序员。
场景:找所有邮件地址
text = """
哥哥口袋有糖
初识物联 1
346504108@qq.com
儒雅的刘飞 3
初识物联 1
397872410@qq.com,谢谢楼主
该来的总会来
物联博士 5
1459543548@qq.com
谢谢谢谢
pyxxponly@qq.com
BLACKPINK_罗捷
深入物联 2
1228074244@qq.com
"""
为了图简单,当然也可以手动进行复制,一旦内容很多就比较麻烦了。这时候我们就可以使用正则表达式进行快速处理。
346504108@qq.com
397872410@qq.com
1459543548@qq.com
pyxxponly@qq.com
1228074244@qq.com
元字符
使用元字符匹配单个字符
字符 | 功能 |
---|---|
\d | 匹配数字,即 0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即空格、tab 键等空白字符 |
\S | 匹配非空白 |
\w | 匹配单词字符,即 a-z、A-Z、0-9、_ |
\W | 匹配非单词字符 |
. | 匹配任意 1 个字符(除了\n) |
* | 匹配前一个字符出现 0 次或者无限次,即可有可无 |
+ | 匹配前一个字符出现 1 次或者无限次,即至少有 1 次 |
[ ] | 匹配[ ] 中列举的字符 |
匹配数字
import re
res = re.findall("\d", '346504108@qq.com')
print(res)
运行结果:
['3', '4', '6', '5', '0', '4', '1', '0', '8']
匹配多个字符
import re
res = re.findall("\d+", '346504108@qq.com')
print(res)
运行结果:
['346504108']
.*
匹配任意多个字符
思考:匹配 Hello
与 Demo
之间的内容
import re
content = 'Hello World This is a Regex Demo'
result = re.findall('Hel.* Regex Demo', content)
print(result)
字符集
可能会出现的一些情况
[123456zxcv]
字符集只能匹配一个出现在集合里面的值
\d
代表 0-9
的所有数字
[0123456789]
与 \d
等效
import re
res = re.findall("\d+", '346504108@qq.com')
print(res)
res1 = re.findall("[0123456789]+", '346504108@qq.com')
print(res1)
运行结果:
['346504108']
['346504108']
思考:邮箱可能出现为字符串,该如何处理?
例如:yanglong985@163.com
提示:[a-z]
案例:匹配手机号
匹配中国电信手机号码
- 中国电信号段
133. 153. 180. 189
- 号码总长度为 11 位
实现方式:
- 编写电信号码的正则
- 进行匹配
- 打印结果
import re
str_phone = """13357024777
电信 浙江省 衢州 尾数 AAA 号码吉凶
18948121234
电信 广东省 茂名 尾数 ABCD 号码吉凶
13873179698
移动 湖南省 长沙 个性靓号 号码吉凶
15802648889
移动 湖南省 长沙 尾数 AAAB 号码吉凶
"""
# 第一位都是以 1 开头 第二位可以为 34578 第三位没有 6 与 9 后面都是数字
res = re.findall('1[358][039]\d+', str_phone)
print(res)
数量词
使用数量词匹配多个字符
字符 | 功能 |
---|---|
{m} | 匹配前一个字符出现 m 次 |
{m,n} | 匹配前一个字符出现从 m 到 n 次 |
需求:匹配出,8 到 20 位的密码,可以是大小写英文字母、数字、下划线
# coding=utf-8
import re
res = re.findall("[a-zA-Z0-9]{8}", "ash2e223 3424kjkljkljf 34523nmkdsjf")
print(res)
ret = re.findall("[a-zA-Z0-9_]{8,20}", "ash2e223 3424kjkljkljf 34523nmkdsadsjf")
print(ret)
案例:QQ
号码匹配
QQ
号规则- 第一位数字不能为 0
- 可能是 5-12 位
import re
qq_str = """
346504108@qq.com
Super 劫 Zed: 540775360@qq.com
397872410@qq.com,谢谢楼主
1459543548@qq.com
"""
ret = re.findall("[1-9][0-9]{4,11}", qq_str)
print(ret)
运行结果:
['346504108', '540775360', '397872410', '1459543548']
精确匹配与泛匹配
泛匹配
泛匹配是匹配所有的东西
import re
content = 'Hello World This is a Regex Demo'
result = re.findall('Hello.*Demo', content)
print(result)
精确匹配
精确匹配是匹配括号里面的东西
import re
content = 'Hello World This is a Regex Demo'
result = re.findall('Hello (\d+).*Demo', content)
print(result)
贪婪匹配与非贪婪匹配
Python 里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;
非贪婪则相反,总是尝试匹配尽可能少的字符。
在"*","?","+","{m,n}"
后面加上 ?
,使贪婪变成非贪婪。
import re
content = 'Hello World This is a Regex Demo. This is a Regex Demo'
result = re.findall('Hel.*(\d+) Regex Demo', content)
print(result)
解决方式:非贪婪操作符“?”,这个操作符可以用在"*","+","?"的后面,要求正则匹配的越少越好。
result2 = re.findall('Hel.*(\d+)? Regex Demo', content)
print(result2)
案例:匹配文字数据
"""
1. 提取 a 标签里面的电影名字信息
2. 提取 title 后面的文字信息
"""
html = """
<a href="/films/488" title="机器人总动员" data-act="boarditem-click" data-val="{movieId:488}">机器人总动员</a>
<a href="/films/78312" title="时空恋旅人" data-act="boarditem-click" data-val="{movieId:78312}">时空恋旅人</a>
<a href="/films/491" title="神偷奶爸" data-act="boarditem-click" data-val="{movieId:491}">神偷奶爸</a>
"""
参考答案
import re
html = """
<a href="/films/488" title="机器人总动员" data-act="boarditem-click" data-val="{movieId:488}">机器人总动员</a>
<a href="/films/78312" title="时空恋旅人" data-act="boarditem-click" data-val="{movieId:78312}">时空恋旅人</a>
<a href="/films/491" title="神偷奶爸" data-act="boarditem-click" data-val="{movieId:491}">神偷奶爸</a>
"""
# re.S 最好默认就加上,因为有些匹配的内容会进行换行
# print(re.findall('<a .*>(.*)</a>', html, re.S))
# print(re.findall('<a .*?>(.*)</a>', html, re.S))
print(re.findall('<a .*?>(.*?)</a>', html, re.S))
print(re.findall("title='(.*?)'", html, re.S))
# 内容里面是双引号
print(re.findall('title="(.*?)"', html, re.S))
匹配开头结尾
字符 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
末尾匹配
需求:匹配 163.com 的邮箱地址
import re
email_list = ["xiaoWang@163.com", "xiaoWang@163.comheihei", ".com.xiaowang@qq.com"]
for email in email_list:
ret = re.match("[\w]{4,20}@163\.com", email)
if ret:
print("%s 是符合规定的邮件地址,匹配后的结果是:%s" % (email, ret.group()))
else:
print("%s 不符合要求" % email)
运行结果:
['xiaoWang@163.com']
[]
[]
完善后
email_list = ["xiaoWang@163.com", "xiaoWang@163.comheihei", ".com.xiaowang@qq.com"]
for email in email_list:
ret = re.match("[\w]{4,20}@163\.com$", email)
if ret:
print("%s 是符合规定的邮件地址,匹配后的结果是:%s" % (email, ret.group()))
else:
print("%s 不符合要求" % email)
运行结果:
xiaoWang@163.com 是符合规定的邮件地址,匹配后的结果是:xiaoWang@163.com
xiaoWang@163.comheihei 不符合要求
.com.xiaowang@qq.com 不符合要求
案例 - 匹配品牌号
匹配每一条数据的品牌 中文、英文需要进行区分为要进行区分
提示
[\u4e00-\u9fa5]
可以匹配中文
import re
csv_str = """凯迪拉克 ATS-L 2016 款 28T 时尚型,2016 年,2.5 万公里,长沙,16.77 万,34.60 万
奥迪 A6L 2014 款 TFSI 标准型,2014 年,13.8 万公里,长沙,21.96 万,44.50 万
本田 思域 2016 款 1.8L 自动舒适版,2016 年,4.8 万公里,长沙,8.87 万,15.20 万
大众 朗逸 2015 款 1.6L 自动舒适版,2016 年,10.5 万公里,长沙,7.27 万,14.90 万
凯迪拉克 ATS-L 2016 款 28T 时尚型,2016 年,2.5 万公里,长沙,16.77 万,34.60 万
奥迪 A6L 2014 款 TFSI 标准型,2014 年,13.8 万公里,长沙,21.96 万,44.50 万
本田 思域 2016 款 1.8L 自动舒适版,2016 年,4.8 万公里,长沙,8.87 万,15.20 万
大众 朗逸 2015 款 1.6L 自动舒适版,2016 年,10.5 万公里,长沙,7.27 万,14.90 万
凯迪拉克 ATS-L 2016 款 28T 时尚型,2016 年,2.5 万公里,长沙,16.77 万,34.60 万
奥迪 A6L 2014 款 TFSI 标准型,2014 年,13.8 万公里,长沙,21.96 万,44.50 万
本田 思域 2016 款 1.8L 自动舒适版,2016 年,4.8 万公里,长沙,8.87 万,15.20 万
大众 朗逸 2015 款 1.6L 自动舒适版,2016 年,10.5 万公里,长沙,7.27 万,14.90 万
别克 君威 2014 款 GS 2.0T 燃情运动版,2015 年,3.0 万公里,长沙,补贴后 11.97 万,
Smart smart fortwo 2012 款 1.0 MHD 硬顶标准版,2014 年,5.6 万公里,长沙,4.89 万,12.50 万"""
参考答案
# 首尾匹配是针对一串字符串
for line in csv_str.split('\n'):
r = re.findall('^[\u4e00-\u9fa5]+', line)
# 如果中文品牌有内容,直接打印
if r:
print(r, line)
else:
r = re.findall('^[a-zA-Z]+', line)
print(r, line)
r 的作用
>>> mm = "c:\\a\\b\\c"
>>> mm
'c:\\a\\b\\c'
>>> import re
>>> re.findall("c:\\\\",mm)
['c:\\']
>>> re.findall(r"c:\\",mm)
['c:\\']
Python 中字符串前面加上 r 表示原始字符串,与大多数编程语言相同,正则表达式里使用 \
作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符 \
,那么使用编程语言表示的正则表达式里将需要 4 个反斜杠 \\
:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。
Python 里的原生字符串很好地解决了这个问题,有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
匹配分组
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
(a|b) | 将括号中字符作为一个分组 |
示例 1:
需求:匹配出所有的 gmail 与 163 邮箱
import re
content = """
346504108@gmail.com
540775360@gmail.com
123123360@gmail.com
675454350@163.com
234234360@163.com
234237760@163.com
onlyasd40@163.com
23443f548@163.com
354573548@qq.com
345436548@qq.com
pyxxponly@qq.com
122874244@qq.com
"""
# 分组匹配
print(re.findall('[0-9a-zA-Z]+@163.com|[0-9a-zA-Z]+@gmail.com', content))
# 精确匹配
print(re.findall('[0-9a-zA-Z]+@(163.com|gmail.com)', content))
# 第二种分组匹配
print(re.findall('[0-9a-zA-Z]+@(?:163.com|gmail.com)', content))