前言:正则匹配的一些相关知识点
应用场景如下:
- 特定规律字符串的查找、切割、替换等
- 特定格式(邮箱、手机号、IP、URL)等的校验
- 爬虫项目中,提取特定内容
使用原则:
- 只要使用字符串函数能够解决的问题就不要使用正则
- 正则的效率比较低,同时会降低代码的可读性
基本使用
- 需要借助re模块
- 相关函数:
-
match: 只从开头进行匹配,匹配到的就返回结果对象,没有找到就返回None
-
search: 从任意位置匹配,功能同上,单次匹配
m = re.search('abc', 'hadajabcadjlae') if m: # 返回匹配的内容 print(m.group()) # 返回匹配内容的位置 print(m.span())
-
findall: 全部匹配,返回所有匹配到的结果,没有找到返回空列表
f = re.findall('abcd', 'abcasjdlaabcaksjd;abcasdjla') print(f)
-
compile:创建正则表达式对象,可以让创建正则对象和内容匹配分开操作
# 可以先生成正则表达式对象,然后再进行匹配 c = re.compile('cba') # print(type(c)) # 从开头匹配 # m = c.match('cbasdhaj;acbaalsdk') # 从任意位置匹配 m = c.search('casdhaj;acbaalsdk') if m: print(m.group()) # 匹配所有 f = c.findall('ahkjdcbasdkjalcbasakldjacba') print(f)
此方式可以分开操作,方式比较灵活
-
正则语法
-
单个字符
普通字符:简单理解就是一对一的完全匹配 []:中间的任意一个字符 [abc]:abc的任意一个字符 [0-9]:任意的数字字符 [a-zA-Z]:任意的字母 [^0-9]:非数字字符 . :除'\n'以外的任意字符 \d:数字字符,等价于[0-9] \D:非数字字符,等价于[^0-9] \w:匹配字(数字、字母、下划线) \W:匹配非字(\w相反的内容) \s:空白字符(\n、\r、\t、空格) \S:非空白字符(\s相反的内容) \b:词边界(开头、结尾、空格、标点) \B:非词边界(\b相反的内容)
-
次数限定
*:任意次 +:至少一次 ?:至多一次 {m}:指定m次 {m,n}:m <= 次数 <=n {m,}:至少m次 {,m}:至多m次
-
边界限定
-
^:以指定的内容开头
-
$: 以指定的内容结尾
-
示例
import re # 只从开头匹配 # f = re.findall('^hello', 'asjdhelloaskd') # 只从结尾匹配 f = re.findall('world$', 'asjdhelloaskworld') # 同时限制开头和结尾 f = re.findall('^\w*$', 'asjdhelloaskworld') print(f)
-
优先级与整体
import re # |:表示或,拥有最低的优先级 # ():可以表示一个整体 s = re.search('hell(o|w)orld', 'akdhahellworld') if s: print(s.group())
-
-
分组匹配
-
示例1
import re c = re.compile('(\d+)([a-z]+)(\d+)') s = c.search('agaj2635sdhasda237adjsd') if s: # 0:表示完整匹配内容,之后的数字表示第几组,也就是第几个()匹配的内容 print(s.group(0), s.span(0)) print(s.group(1), s.span(1)) print(s.group(2), s.span(2)) print(s.group(3), s.span(3))
输出结果是: 2635sdhasda237 (4, 18) 2635 (4, 8) sdhasda (8, 15) 237 (15, 18)
-
示例2
import re # 固定匹配 # c = re.compile('<div><a>\w+</a></div>') # 动态匹配:匹配两次嵌套的标签 # c = re.compile('<[a-z]+><[a-z]+>\w+</[a-z]+></[a-z]+>') # 无名分组:\1、\2分别表示前面的第一组、第二组匹配的内容 # c = re.compile(r'<([a-z]+)><([a-z]+)>\w+</\2></\1>') # 命名分组:给分组()起名字 c = re.compile(r'<(?P<one>[a-z]+)><(?P<two>[a-z]+)>\w+</(?P=two)></(?P=one)>') s = c.search('<div><a>百度一下</a></div>') if s: print(s.group()) # 返回所有组的信息,是一个元组 print(s.groups()) # 返回分组的字典,键是组的名字,值时匹配的内容 print(s.groupdict())
输出结果: <div><a>百度一下</a></div> ('div', 'a') {'one': 'div', 'two': 'a'}
-
-
findall
import re # 按照正则进行匹配,但是添加()后,结果只显示()匹配的内容 f = re.findall('A(abc)A', 'asdjAabcAasdjAabcAsdkabca') print(f)
输出结果: ['abc', 'abc']
-
贪婪匹配
- 贪婪:最大限度的匹配,正则匹配默认是贪婪的
- 非贪婪:只要满足条件,能少匹配就少匹配。可以使用‘?’来取消贪婪匹配
- 示例
import re # .+?:取消至少一次的贪婪匹配 # c = re.compile('a.+?b') # .*?:取消任意多次的贪婪匹配 c = re.compile('a.*?b') s = c.search('sdhaasdajasksdbsdjbsdk') if s: print(s.group())
输出结果: aasdajasksdb
-
匹配模式
- 说明:匹配模式就是对默认的匹配原则进行整体的修饰
- 示例:
import re # re.I:表示忽略大小写 # s = re.search(r'hello', 'Hello', re.I) # re.M:多行处理,默认会把字符当做一行处理 s = re.search(r'^hello', 'asdkasj\nhelloajsdhkas', re.M) # string = '<div>hello</div>' string = '''<div> hello </div>''' # re.S:是.可以匹配任意,作为单行处理(忽略\n) s = re.search(r'<div>.*?</div>', string, re.S) if s: print(s.group())
输出结果: <div> hello </div>
-
字符转义
-
匹配凡是跟正则语法相关的字符都需要进行转义
-
示例:
import re # python \\\d \\\\d r'\\d' # re \\d \\d \\d # 实际 \d \d \d # 推荐使用,否则需要写很多\ c = re.compile(r'\\d') s = c.search('\d') if s: print(s.group()) # 匹配'\b'字符串 c = re.compile(r'\b') # \b本身有特殊含义 s = c.search(r'\b') if s: print('成功', s.group())
在定义字符串的开头添加’r’表示原始字符串,可以轻松解决很多关于转义的问题
-
-
正则切割
import re c = re.compile(r'\d') string = '正则其实不难1但是学完之后2发现什么也不出来3是这样吧' # 字符串是固定切割,不能解决某类的匹配切割问题 # print(string.split('1')) # 按照正则进行切割 ret = c.split(string) print(ret) # 整体方案 print(re.split(r'\d', string))
输出结果: ['正则其实不难', '但是学完之后', '发现什么也不出来', '是这样吧'] ['正则其实不难', '但是学完之后', '发现什么也不出来', '是这样吧']
-
正则替换
string = 'helloadjasdhelloskdjalkhellosdjka' # 字符串替换 new_string = string.replace('hello', 'world', 2) print(new_string) import re s = 'how1are2you' # 正则替换 # s2 = re.sub(r'\d', ' ', s) # 替换时可以传递一个函数,使用函数的返回值进行替换 def double(s): return str(int(s.group()) * 2) # return 'xxx' # 使用专门的处理函数,可以认为的干预替换过程 s2 = re.sub(r'\d', double, s) print(s2)
输出结果: how2are4you