''''''
'''
正则就是用来筛选字符串中的特定的内容的
书:正则指引
正则的应用场景
1.爬虫
2.数据分析
只要是reg...一般情况下都是跟正则有关
正则表达式
正则测试网站 : http://tool.chinaz.com/regex/
字符组:
[0123456789] 也可以写成 [0-9]
待匹配字符组 : 8 匹配结果: True
待匹配字符组 : a 匹配结果: False
解释: 方括号中列举所有数字,关系是或,一个字符串里面的表达式都是或的关系
即判断待匹配字符组中 有没有 0或者1或者2或者3或者4或者5或者6...
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符和"待匹配字符"相同都视为可以匹配
[a-z] [A-Z] 根据ASCII码表对应的数值从小到大排序
[0-9a-fA-F] 同理,两个区间之间紧挨着就可以
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线
\W 匹配非字母或数字或下划线
\s 匹配任意的空白符
\S 匹配非空白符
\d 匹配数字
\D 匹配非数字
^ 匹配字符串的开始
$ 匹配字符串的结尾
^与$符连用 会精准限制匹配的内容,以什么开头,以什么结尾
两者中间写什么 匹配的字符串就必须是什么,多一个也不想少一个也不行
[...] 匹配字符组中的字符 ^ 直接写在外面 限制字符串的开头
[^...] 匹配除了字符组中字符的所有字符 ^ 直接写在外面 限制字符串的开头
() 匹配括号内的表达式,也表示一个组, 分组在正则的语法中就是()
分组:当多个正则符号需要重复多次的时候或者当做一个整体进行其他操作,那么可以分组的形式
\n 匹配一个换行符
\t 匹配一个制表符
\b 匹配一个以什么结尾的单词
a|b 匹配字符a或字符b
abc|ab 一定要将长的放在前面
量词 量词必须跟在正则符号的后面 量词只能能够限制紧挨着它的那一个正则符号
* 重复零次或更多次 [0,+∞)
? 重复零次或一次 [0,1]
+ 重复一次或更多次 [1,+∞)
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
正则 待匹配字符 匹配结果 说明
海. 海燕海娇海东 海燕海娇海东 匹配所有"海."的字符,
# 匹配到三条结果,海开头,除换行符以外的任意一个字符,因为只有一个.
海.. 海燕海娇海东 海燕海 匹配所有"海.."的字符,
# 匹配到一条结果,海开头,加 除换行符以外的任意两个字符,因为有两个个.,那只有一个结果
正则 待匹配字符 匹配结果 说明
^海. 海燕海娇海东 海燕 只从开头匹配"海."
# 匹配到一条结果,海开头,除换行符以外的任意一个字符
^海.. 海燕海娇海东 海燕海 只从开头匹配"海.."
# 匹配到一条结果,整个待匹配字符是一个字符串,只从开头开始匹配,
# 从待匹配字符开头的海开头,加后面除换行符以外的任意两个字符,因为有两个个.
正则 待匹配字符 匹配结果 说明
海.$ 海燕海娇海东 海东 只匹配结尾的"海.$"
# 匹配到一条结果,整个待匹配字符是一个字符串,匹配 以海 什么 结尾,因为只有一个.
海..$ 海燕海娇海东 None(无) 只匹配结尾的"海..$"
# 匹配到零条结果,整个待匹配字符是一个字符串,匹配 以海 什么什么 结尾,因为有两个.
# 海娇海东 这个结果是 海...$ 的匹配结果 海东 这个结果是 海.$ 的匹配结果
. 匹配除换行符以外的任意字符
量词
? 重复零次或一次 [0,1]
* 重复零次或更多次 [0,+∞)
+ 重复一次或更多次 [1,+∞)
{n,m} 重复n到m次
{n} 重复n次
{n,} 重复n次或更多次
正则 待匹配字符 匹配结果 说明
李.? 李杰和李莲英和李二棍子 李杰 李莲 李二 ?表示重复零次或一次,即只匹配"李"后面一个任意字符
# 匹配到三条结果,匹配效果跟 李. 一样,匹配 李+后面任意一个字符 ,?表示重复零次或一次,贪婪匹配,匹配一次
正则 待匹配字符 匹配结果 说明
李.* 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子 *表示重复零次或多次,即匹配"李"后面多个任意字符
# 匹配到一条结果,匹配 李+后面除换行符任意多个字符 ,*表示重复零次或多次,贪婪匹配,匹配多次,获得多个任意字符除换行符
正则 待匹配字符 匹配结果 说明
李.+ 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子 +表示重复一次或多次,即匹配"李"后面多个任意字符
# 匹配到一条结果,匹配 李+后面除换行符任意多个字符 ,*表示重复一次或多次,贪婪匹配,匹配多次,获得多个任意字符除换行符
正则 待匹配字符 匹配结果 说明
李.{1,2} 李杰和李莲英和李二棍子 李杰和 李莲英 李二棍 {1,2}表示重复一次或两次,即只匹配"李"后面两个个任意字符
# 匹配到三条结果,匹配 李+后面除换行符 一个或两个字符 ,{1,2}表示重复一次或两次,贪婪匹配,匹配两次,获得多个任意字符除换行符
注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配
正则 待匹配字符 匹配结果 说明
李.*? 李杰和李莲英和李二棍子 李 李 李 *表示重复零次或多次,加?惰性匹配就匹配0次
# 匹配到三条结果,匹配 李+后面除换行符 零个或两个字符 ,加?惰性匹配就匹配0次,也就匹配个李,.不起作用
正则 待匹配字符 匹配结果 说明
李[杰莲英二棍子]* 李杰和李莲英和李二棍子 李杰 李莲英 李二棍子 匹配"李"字后面[杰莲英二棍子]的字符任意次,没有和
# 匹配到三条结果,匹配"李"字后面[杰莲英二棍子]的字符任意次,没有和,也就匹配出了三个结果
正则 待匹配字符 匹配结果 说明
李[^和]* 李杰和李莲英和李二棍子 李杰 李莲英 李二棍子 表示匹配一个不是"和"的字符任意次
# 匹配到三条结果,除了 和 以外的任意字符,即取到了三条结果
正则 待匹配字符 匹配结果 说明
[\d] 456bdha3 4 5 6 3 四个结果 表示匹配任意一个数字,匹配到4个结果
# 匹配到四条结果,[\d] 表示匹配任意一个数字,爱个字符匹配,一致匹配到结束,
正则 待匹配字符 匹配结果 说明
[\d]+ 456bdha3 456 3 两个结果 表示匹配任意个数字,匹配到2个结果
# 匹配到两条结果,[\d]是匹配任意数字,+是匹配一次或者多次,前三个数字连续匹配了任意数字一次或者三次
# 然后继续匹配,后面这个数字,匹配了一次或者多次
^[1-9]\d{14}(\d{2}[0-9x])?$
^[1-9] 以1-9任意一个数字开头,匹配开头1位
\d{14} 匹配任意数字14次,即14位
(\d{2}[0-9x])?$ 括号内 \d{2} 匹配任意数字两次,即2位,[0-9x] 匹配0-9和X任意一个,匹配一次,即1位
括号表示分组,括号后面表示该括号内匹配零次或者一次,匹配零次就是前15位,匹配15位身份证
匹配1次就是匹配括号内的3位数,加前面的15位就是18位身份证
$表示以这个匹配结果结尾
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$
^表示以括号的分组开头,括号内有一个|,或符号
左边 [1-9]\d{16}[0-9x] 1+16+1=18位
[1-9] 以1-9任意一个数字开头,匹配开头1位
\d{16} 匹配任意数字16次,即16位
[0-9x] [0-9x] 匹配0-9和X任意一个,匹配一次,即1位
右边 [1-9]\d{14} 1+14=15 位
[1-9] 以1-9任意一个数字开头,匹配开头1位
\d{14} 匹配任意数字14次,即14位
最后括号匹配左边就是18位,以18位开头以18位结尾,匹配一个身份证号码
括号匹配右边边就是15位,以15位开头以15位结尾,匹配一个身份证号码
贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配
非贪婪匹配:量词后加上?问好,在满足匹配时,匹配尽可能少的字符串
正则 待匹配字符 匹配结果 说明
<.*> <script>...<script> <script>...<script> 默认为贪婪匹配模式,会匹配尽量长的字符串
# 贪婪匹配会.*表示任意字符匹配0次或者多次,即 script>...<script 这个是在<>中的任意字符匹配尽可能多次的结果
正则 待匹配字符 匹配结果 说明
<.*?> <script>...<script> <script> <script> 加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串
# 非贪婪匹配会.*表示任意字符匹配0次或者多次,?即尽可能少的匹配,即 script 是尽可能少的第一次在<>中的匹配内容,可以匹配到两次
# 效果跟 正则 : <.+?> 一样
几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
合在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?X : 就是取前面任意长度的字符,直到一个x出现
转义符
正则 待匹配字符 匹配结果 说明
\n \n False 因为在正则表达式中\是有特殊意义的字符,所以要匹配\n本身,用表达式\n无法匹配
\\n \n True 转义\之后变成\\,即可匹配
"\\\\n" '\\n' True 如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次
r'\\n' r'\n' True 在字符串之前加r,让整个字符串不转义
单右斜杠是转义符,\n是换行符,需要取消转义,\\n就可以转义匹配字符,如果有两个右斜杠就需要两个右斜杠转义,就是四个右斜杠
r前缀加引号,这个也是转义符,引号中放置需要转义的字符串
'''
'''
re模块下的常用方法(re模块中正则表达式的应用)
re模块与正则表达式之间的关系
正则表达式不是python独有的
它是一门独立的技术
所有的编程语言都可以使用正则
但是如果你想在python中使用, 你就必须依赖于re模块
'''
import re
"""
re.findall
re.search
re.match
"""
'''findall'''
# # re.findall
# res1 = re.findall('[a-z]','eva egon jason') # 调用re模块提供的findall方法,正则一个一个匹配,一次匹配一个
# print(res1)
# # ['e', 'v', 'a', 'e', 'g', 'o', 'n', 'j', 'a', 's', 'o', 'n']
# res2 = re.findall('[a-z]+','eva egon jason') # 调用re模块提供的findall方法,+号一次匹配多个或者一个
# print(res2)
# # ['eva', 'egon', 'jason']
# res3 = re.findall('[a-z]+?','eva egon jason') # 调用re模块提供的findall方法,+号一次匹配多个或者一个
# print(res3)
# # ['e', 'v', 'a', 'e', 'g', 'o', 'n', 'j', 'a', 's', 'o', 'n'] # 加问号,每次最少匹配非贪婪匹配
# # findall('正则表达式','带匹配的字符串')
# # 找出字符串中符合正则表达式全部内容 并且返回的是一个列表,列表中的元素就是正则匹配到的结果
'''re.search'''
"""
注意:
1.search只会依据正则查一次 只要查到了结果 就不会再往后查找
2.当查找的结果不存在的情况下 调用group直接报错
"""
# res = re.search('a','eva egon jason') # 调用re模块的search函数
# # search('正则表达式','带匹配的字符串')
# print(res) # search不会给你直接返回匹配到的结果 而是给你返回一个对象
# # <_sre.SRE_Match object; span=(2, 3), match='a'>
# print(res.group()) # 必须调用group才能看到匹配到的结果
# # a 返回的结果是正则匹配后的结果,search只会依据正则查一次 只要查到了结果 就不会再往后查找
# res1 = re.search('k','eva egon jason')
# print(res1)
# # 如果正则匹配的结果不存在,没有匹配到,则函数返回None
# # print(res1.group())
# # 如果search返回的None,即正则匹配没有找到,调用group就会报错 'NoneType' object has no attribute 'group'
# if res1: # 解决这个问题,可以用if语句判断,如果返回None,则自带布尔值False,则不会执行group
# print(res1.group()) # 反之,不位None,布尔值为True,就会执行group查看结果
# re.match
"""
注意:
1.match只会匹配字符串的开头部分,开头不对就返回None,就没找到
2.当字符串的开头不符合匹配规则的情况下 返回的也是None 调用group也会报错
"""
# res1 = re.match('e','eva egon jason') # 调用re模块的match函数,只会匹配字符串的开头部分
# print(res1) # 开头不对就返回None,就没找到
# # <_sre.SRE_Match object; span=(0, 1), match='e'> 返回一个对象
# print(res1.group())
# # e
# res2 = re.match('ev','eva egon jason')
# res3 = re.match('eva','eva egon jason')
# print(res2) # <_sre.SRE_Match object; span=(0, 2), match='ev'>
# print(res3) # <_sre.SRE_Match object; span=(0, 3), match='eva'>
# print(res2.group()) # ev
# print(res3.group()) # eva
# res4 = re.match('a','eva egon jason')
# print(res4) # 如果找的值不存在,则返回None
# # print(res3.group()) # 这个会报错,因为正则找不到值,函数返回None,返回值None没有group方法,会报错
'''
re.split('[ab]', 'abcd') 先以a作为分隔符,分割出来是[]['bcd'],然后再按b分割,就是[] [] ['cd'],做成一个列表
re.split('[分隔符可以写多个,依次按分隔符切分]', '待匹配字符')
'''
# ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
# # 先以a作为分隔符,分割出来是[]['bcd'],然后再按b分割,就是[] [] ['cd'],做成一个列表
# print(ret) # ['', '', 'cd'] 返回的还是列表
# ret=re.split("\d+","eva3egon4yuan")
# # 切分字符串,切分分隔符为正则结果,匹配一次或者多次数字,把所有数字当作分割符并且都切掉
# print(ret) #结果 : ['eva', 'egon', 'yuan']
#
# ret1=re.split("(\d+)","eva3egon4yuan")
# # 切分字符串,切分分隔符为分组正则结果,匹配一次或者多次数字,把所有数字当作分割符并且保留分组正则结果,即保留数字
# print(ret1) #结果 : ['eva', '3', 'egon', '4', 'yuan']
"""
# re.sub()先按照正则表达式查找所有符合该表达式的内容 统一替换成'新的内容' 还可以通过n来控制替换的个数
re.sub('正则表达式','需要替换的新字符','待匹配字符','替换次数')
"""
# ret1 = re.sub('\d', 'H', 'eva3egon4yuan4',1) # 匹配全数字,将数字替换成'H',待匹配字符,参数1表示只替换1个
# # sub('正则表达式','新的内容','待替换的字符串',n)
# print(ret1) # evaHegon4yuan4
# ret2 = re.sub('\d', 'H', 'eva3egon4yuan4')
# print(ret2) # evaHegonHyuanH 不写替换次数,默认全部替换
# ret1 = re.sub('\d', 'H', 'eva3egon4yuan4',10) # 匹配全数字,将数字替换成'H',待匹配字符,参数1表示只替换1个
# # sub('正则表达式','新的内容','待替换的字符串',n)
# print(ret1) # evaHegonHyuanH # 替换次数超过最大次数,替换全部即可,不会报错
# ret = re.subn('\d', 'H', 'eva3egon4yuan4') # 将数字替换成'H',返回元组(替换的结果,替换了多少次)
# ret1 = re.subn('\d', 'H', 'eva3egon4yuan4',1) # 将数字替换成'H',返回元组(替换的结果,替换了多少次)
# print(ret) # 返回的是一个元组 元组的第二个元素代表的是替换的个数 # ('evaHegonHyuanH', 3)
'''
obj = re.compile('\d{3}') #将正则表达式\d{3}编译成为一个 正则表达式对象obj,方便后面调用
正则表达式对象名 = re.compile('正则表达式') 可以直接调用 正则表达式对象名.search方法
正则表达式对象名.match 正则表达式对象名.findall
'''
# obj = re.compile('\d{3}') #将正则表达式编译成为一个 正则表达式对象,规则要匹配数字,匹配三次,一次三个
# ret = obj.search('abc123ee22ee') #正则表达式对象调用search,参数为待匹配的字符串
# print(ret.group()) #结果 : 123 因为22不满足连续匹配三次,所以没有,需要是哪个而连着的数字
#
# res1 = obj.findall('3479827347293498273841')
# print(res1) #结果 : ['347', '982', '734', '729', '349', '827', '384'] 最后一个1也不满足连续匹配三次
'''
re.finditer('正则','待匹配字符') 返回一个迭代器
'''
# import re
# ret = re.finditer('\d', 'ds3sy4784a56') #finditer返回一个存放匹配结果的迭代器
# print(ret) # <callable_iterator object at 0x10195f940>
# print(next(ret).group()) # 等价于ret.__next__() 3
# print(next(ret).group()) # 等价于ret.__next__() 4
# print(next(ret).group()) # 等价于ret.__next__() 7
# print(next(ret).group()) # 等价于ret.__next__() 8
# print(next(ret).group()) # 等价于ret.__next__() 4
# # print(next(ret).group()) # 等价于ret.__next__() 查出迭代取值的范围 直接报错
# print(next(ret).group()) #查看第一个结果 # 前面被取了四次,这次再调next取第五个,并且group返回结果
# print(next(ret).group()) #查看第二个结果 # 前面被取了五次,这次再调next取第六个,并且group返回结果
# print([i.group() for i in ret]) #查看剩余的左右结果 打印继续取值的结果,前面六次取完了,所以结果为[]
'''
?P<别名> 可以通过这个方法给一个分组的正则表达式取别名,并且这个别名可记录这个分组的匹配结果
通过res.group('别名') 查看 这个别名分组的匹配结果
或者按别名的索引取 别名分组的匹配结果 res.group('别名索引(从1开始)')
'''
# import re # 导入模块
# res1 = re.search('^[1-9](\d{14})(\d{2}[0-9x])?$','110105199812067023') # 匹配身份证?匹配0此或者一次,15或者18位
# print(res1.group()) # 110105199812067023
# # 还可以给某一个正则表达式起别名
# res2 = re.search('^[1-9](?P<password>\d{14})(?P<username>\d{2}[0-9x])?$','110105199812067023')
# print(res2.group('password')) # 10105199812067
# print(res2.group(1)) # 10105199812067
# print(res2.group('username')) # 023
# print(res2.group(2)) # 023
'''
字符(正则)字符 返回 ['正则结果']
findall会优先把匹配结果分组里内容返回
如果想要匹配结果,取消权限即可,在分组的开头加上 ?:
字符(?:正则)字符 返回 ['字符正则结果字符']
findall
本身没有group方法,对于分组的正则直接返回分组内匹配到的结果 >>>:分组优先
取消findall默认的分组优先机制 只需要在分组括号最前面加 ?:
search
支持group()方法取值 对于分组的正则表达式 在没有起别名的情况 按照从左到右的顺序 依次取括号内正则匹配到的内容
当给分组内的正则起别名 可以通过别名的方式获取分组内正则表达式匹配到的值
起别名的方式
(?P<user_id>\d+)(?P<username>\w+)
match
对分组有优先级展示的区别
'''
# ret1 = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
# print(ret1) # ['oldboy']
# ret2 = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com') # 忽略分组优先的机制
# print(ret2) # ['www.oldboy.com'] 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可