正则表达式
1.什么是正则表达式
"""
定义:
用来约束字符串匹配某种形式的规则
作用;
1.检测某个字符串是否符合规则;(手机号,身份证号是否合法)
2.提取网页字符串中想要的数据.(爬虫中提取天气,幸运的信息)
在线测试工具 http://tool.chinaz.com/regex/
组成: 普通字符和元字符组成
1.普通字符串: 大小写字母和数字
2.元字符具有特殊含义,大体种类分为:
(1)预定义字符集,字符组
(2)量词
(3)边界符
(4)分组
函数:需要导入import re 模块
findall("正则表达式","字符串") 从左到右匹配,找到所有的内容,返回列表
search("正则表达式","字符串") 从左到右匹配,匹配到第一个满足条件的内容直接返回,返回的是对象
"""
2.匹配单个字符 : 预定义字符集和字符组
import re
"""
语法:re.findall("正则表达式","字符串") 返回列表
注意:正则表达式前面加r防止转义,一定不会错
"""
# 1.预定义字符集
# . 匹配任意字符,除了换行符\n
print(re.findall(".","3a\rd")) #['3', 'a', '\r', 'd']
# \d 匹配数字
# \D 匹配非数字
print(re.findall("\d","sd3kj4")) #['3', '4']
print(re.findall("\D","sd3kj4")) #['s', 'd', 'k', 'j']
# \w 匹配字母数字下划线和中文
# \W 匹配非字母数字下划线和中文
print(re.findall("\w","f2_健康*&")) #['f', '2', '_', '健', '康']
print(re.findall("\W","f2_健康*&")) #['*', '&']
# \s 匹配任意的空白符(空格,\r,\t,\n 等)
# \S 匹配任意非空白符
print(re.findall("\s","k d \rd")) #[' ', ' ', '\r']
print(re.findall("\S","k d \rd")) #['k', 'd', 'd']
# \n 匹配换行符
# \t 匹配制表符
strvar = """
11122 3434
"""
print(re.findall(r"\n",strvar)) #['\n', '\n']
print(re.findall(r"\t",strvar)) #['\t', '\t', '\t']
# 2.字符组 默认必须从字符组中选一个
"""
[...] 匹配字符组中的字符
[^..] 匹配非字符组中的字符(^必须写在第一位)
[0-9] 表示 0~9 所有数字
[a-z] 表示所有小写字母
[A-Z] 表示所有大写字母
[0-9a-zA-Z] 表示所有字母数字,该正则可验证十六进制
[\u4e00-\u9fa5] 表示匹配所有汉字
"""
print(re.findall("[12]","d2k43jl1")) #['2', '1']
print(re.findall('a[0-9]b','a1b a2b a3b acb ayb')) #['a1b', 'a2b', 'a3b']
print(re.findall('a[0-9][*#/]b','a1/b a2b a29b '))#['a1/b']
# 3.匹配转义字符
"""
需要(\)进行转义
而且正则表达式和后面字符串都需要加r
"""
# (1)匹配^ - \ 特殊字符
strvar1 = "a^n a-n a\n"
strvar2 = r"a^n a-n a\n" #加r防止转义
print(re.findall(r"a[\^\-\\]n",strvar1)) #['a^n', 'a-n']
print(re.findall(r"a[\^\-\\]n",strvar2)) #['a^n', 'a-n', 'a\\n']
# (2)匹配\b
strvar=r"aed\bkja\b lka\b"
print(re.findall(r"a\\b",strvar))#['a\\b', 'a\\b']
3.多个字符匹配 : 量词
# 1.量词
"""
? : 匹配 0~1 个
+: 匹配 1~+∞ 个
* : 匹配 0~+∞ 即任意个
{n} 匹配n 个
{n,m} 匹配 n~m 个
{n,} 匹配 n~+∞
"""
print(re.findall("a?b","baabab abb")) #['b', 'ab', 'ab', 'ab', 'b']
print(re.findall("a+b","baabab abb")) #['aab', 'ab', 'ab']
print(re.findall("a*b","baabab abb")) #['b', 'aab', 'ab', 'ab', 'b']
print(re.findall("a{2}b","baabab abb")) #['aab']
print(re.findall("a{1,3}b","baabab abb")) #['aab', 'ab', 'ab']
print(re.findall("a{1,}b","baabab abb")) #['aab', 'ab', 'ab']
# 2.贪婪匹配和非贪婪匹配
"""
贪婪模式 :默认向更多次匹配
回溯算法 : 从左向右一直匹配,直到匹配不到了,在回头,把上一次找到的元素返回
非贪婪模式: 默认向更少次匹配
语法:量词 + "?"
例:.*?w 表示匹配任意长度任意字符遇到一个w就立即停止
"""
strvar="刘能和刘老根和刘一手1子2子"
print(re.findall("刘.?",strvar)) #['刘能', '刘老', '刘一']
print(re.findall("刘.??",strvar))#['刘', '刘', '刘']
print(re.findall("刘.+",strvar)) #['刘能和刘老根和刘一手1子2子']
print(re.findall("刘.+?",strvar))#['刘能', '刘老', '刘一']
print(re.findall("刘.*子",strvar)) #['刘能和刘老根和刘一手1子2子']
print(re.findall("刘.*?子",strvar))#['刘能和刘老根和刘一手1子']
# 3.匹配开头结尾->边界符
"""
只要不是字母数字下划线都可以作为边界
边界符\b :
1.\b 可以作为转义字符 ->退格
2.\b 正则中的边界符
例如:word 卡住左边界\bw ,卡主右边界 d\b
注意:正则表达式前需要加r 防止转义
^ :匹配字符串的开始
$ :匹配字符串的结尾
如果使用了^ 和 $ 必须要把字符串看成一个整体
"""
strvar = "word pwd wook"
# 卡住左边界
print(re.findall(r"\bw",strvar))#['w', 'w']
print(re.findall(r"\bp",strvar))#['p']
print(re.findall(r"\bw.*",strvar))#['word pwd wook']
print(re.findall(r"\bw.*?",strvar))#['w', 'w']
print(re.findall(r"\bw\S*",strvar))#['word', 'wook']
# 卡主右边界
print(re.findall(r"d\b",strvar))#['d', 'd']
print(re.findall(r".*d\b",strvar))#['word pwd']
print(re.findall(r".*?d\b",strvar))#['word', ' pwd']
# ^ 匹配字符串的开始 $匹配字符串的结尾
print(re.findall("^w.*",strvar))#['word pwd wook']
print(re.findall("^w.*?",strvar))#['w']
print(re.findall("k$",strvar)) #['k']
print(re.findall(".*k$",strvar))#['word pwd wook']
print(re.findall(".*?k$",strvar))#['word pwd wook']从左到右匹配,结尾结束
4.匹配分组
import re
# 1.匹配分组()表示一个整体
"""
(正则表达式):优先显示括号里的内容
(?:正则表达式):取消优先显示
"""
print(re.findall(".*?_d","jia_d ying_d he_d")) #['jia_d', ' ying_d', ' he_d']
print(re.findall("(.*?)_d","jia_d ying_d he_d"))#['jia', ' ying', ' he']
print(re.findall("(?:.*?)_d","jia_d ying_d he_d"))#['jia_d', ' ying_d', ' he_d']
# 2.| 或
"""
注意:
把较难匹配的内容放到前面,容易匹配的放到后面,才能保证所有元素都可以匹配到
"""
print(re.findall("ab|abc","sabcdfabdabc"))#['ab', 'ab', 'ab']
print(re.findall("abc|ab","sabcdfabdabc"))#['abc', 'ab', 'abc']
strvar="2.3 22 33. ..44 66.66 77"
# 匹配小数
print(re.findall("\d+\.\d+",strvar))#['2.3', '66.66']
# 匹配整数(取值不正确)
print(re.findall("\d+",strvar)) #['2', '3', '22', '33', '44', '66', '66', '77']
# 匹配小数和整数
print(re.findall("\d+\.\d+|\d+",strvar))#['2.3', '22', '33', '44', '66.66', '77']
# 使用分组的形式匹配小数和整数
print(re.findall("\d+(?:\.\d+)?",strvar))#['2.3', '22', '33', '44', '66.66', '77']
# 匹配135或137的手机号
strvar="13546346454 13640393839 13728394583"
print(re.findall("(?:135|137)\d{8}",strvar))#['13546346454', '13728394583']
# 只匹配一个手机号
strvar="13593849384"
print(re.findall("^(?:135|137)\d{8}$",strvar))#['13593849384']
# 匹配www.baidu.com 或者 www.oldboy.com
strvar = "www.baidu.com www.oldboy.com www.bxbxww.com"
lst = re.findall("(?:www)\.(?:baidu|oldboy)\.(?:com)",strvar)
print(lst) #['www.baidu.com', 'www.oldboy.com']
# 3.反向引用:\n引用前面第n个分组匹配到的内容
strvar="<abs>dk3<abs>"
# 把第一个括号里面的数据放到\1引用
obj=re.search(r"<(.*?)>(.*?)<\1>",strvar)
print(obj.group())#获取匹配到的数据
print(obj.groups())#获取分组里面的内容
#4.命名分组
"""
(?P<组名>正则表达式) 给分组起一个名字
(?P=组名) 引用组名,把该组名匹配到的数据放到当前位置
"""
obj=re.search(r"<(?P<tag1>.*?)>(?P<tag2>.*?)<(?P=tag1)>",strvar)
print(obj.group()) #<abs>dk3<abs>
print(obj.groups()) #('abs', 'dk3')
# 4.search函数和findall的区别
"""
findall : 优点:从左到右匹配,找到所有的内容,返回列表
缺点:匹配的内容和分组的内容不能同时显示在同一个界面中
search : 缺点:从左到右匹配,匹配到第一个满足条件的内容直接返回,返回的是对象
优点:可以把分组里的内容和匹配到的内容同时显示在同一个界面中
obj.group() => 获取匹配到的内容
obj.groups() => 获取分组中的内容
"""
obj=re.search("(www)\.(baidu|oldboy)\.(com)",strvar)
print(obj) #返回对象
print(obj.group()) #www.baidu.com
print(obj.groups()) #('www', 'baidu', 'com')
5.正则函数
# 1.findall 匹配字符串相应内容, 返回列表
"""
findall ("正则表达式","字符串")
"""
strvar="sdf23sd"
print(re.findall("\d",strvar)) #['2', '3']
# 2.search 匹配出第一个符合条件的数据返回,返回对象
obj=re.search("\d",strvar)
print(obj.group()) #2
# 3.match 验证用户输入内容
"""
与search使用方法一样,search正则表达式开头加^(以..开头),等价于match
"""
# 4.split ("正则表达式","字符串") 按正则表达式把字符串切割成列表
strvar="aa|bb&cc*dd_ee"
print(re.split("[|&*_]",strvar))#['aa', 'bb', 'cc', 'dd', 'ee']
strvar="aa343bb33214cc"
print(re.split("\d+",strvar)) #['aa', 'bb', 'cc']
# 5.sub 替换 返回被替换的字符串
"""
sub("正则表达式","替换的元素","原字符串",替换次数)
"""
strvar="aa|bb&cc*dd_ee"
print(re.sub("[|*&_]","@",strvar)) #aa@bb@cc@dd@ee
print(re.sub("[|*&_]","@",strvar,2)) #aa@bb@cc*dd_ee
# 6.subn 替换 与sub一样 返回元组
"""
sub("正则表达式","替换的元素","原字符串",替换次数)
返回 : ("被替换的字符串",替换次数)
"""
print(re.subn("[|*&_]","@",strvar,2)) #('aa@bb@cc*dd_ee', 2)
# 7.finditer 匹配字符串中相应内容,返回迭代器
strvar="s234sf"
it=re.finditer("\d",strvar)
print(it) #<callable_iterator object at 0x7f21f3c536a0>
print(next(it)) #<_sre.SRE_Match object; span=(1, 2), match='2'>
"""迭代器里面是类似search对象,需要用group获取内容"""
print(next(it).group()) #3
it=re.finditer("\d",strvar) #重置迭代器
for i in it:
print(i.group())
# 8.compile ("正则表达式")制定一个统一的匹配规则
"""
compile("正则表达式",flags=修饰符) flags默认为0
修饰符:
1.re.I(大写i) 使匹配对大小写不敏感
2.re.M 使每一行都能够单独匹配(多行匹配),影响^和$
3.re.S 使 . 匹配包括换行在内的所有字符
作用:制定一次正则表达式,重复使用,提高执行效率
"""
pattern=re.compile("\d") #制定匹配规则
print(pattern) #re.compile('\\d')
# 使用规则
strvar="ad334j"
print(pattern.findall(strvar)) #['3', '3', '4']
obj=pattern.search(strvar)
print(obj.group()) #3
# 修饰符
# 1.re.I 使匹配对大小写不敏感
strvar="<a>都会尽快</A>"
pattern=re.compile("<a>(.*?)</a>",flags=re.I)
obj=pattern.search(strvar)
print(obj.group())
# 2.re.M 使每一行都能够单独匹配,影响^和$
strvar = """
<a>我是连接</a>
<p>我是段落</p>
<div>我是盒子</div>
"""
pattern=re.compile("^<.*?>.*?<.*?>$",flags=re.M)
print(pattern.findall(strvar))#['<a>我是连接</a>', '<p>我是段落</p>', '<div>我是盒子</div>']
# 3.re.S 使 . 匹配包括换行在内的所有字符
strvar="""
dskfl jdfks
fdj3423me
"""
pattern=re.compile(".*?me",flags=re.S)
obj=pattern.search(strvar)
print(obj.group()) #字符串所有内容,包括换行
# 想要所有修饰符,使用|进行拼接
pattern=re.compile(".*?me",flags=re.I|re.M|re.S)