【一】正则语法
【1】引入
- 一说规则我已经知道你很晕了
- 首先你要知道的是
- 谈到正则,就只和字符串相关了。
- 在我给你提供的工具中,你输入的每一个字都是一个字符串。
- 其次,如果在一个位置的一个值,不会出现什么变化,那么是不需要规则的。
- 比如
- 你要用"1"去匹配"1"
- 或者用"2"去匹配"2"
- 直接就可以匹配上。
- 这连python的字符串操作都可以轻松做到。
- 那么在之后我们更多要考虑的是在同一个位置上可以出现的字符的范围。
【2】字符组
正则 |
待匹配字符 |
匹配结果 |
说明 |
[0123456789] |
8 |
True |
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符和"待匹配字符"相同都视为可以匹配 |
[0123456789] |
a |
False |
由于字符组中没有"a"字符,所以不能匹配 |
[0-9] |
7 |
True |
也可以用-表示范围,[0-9]就和[0123456789]是一个意思 |
[a-z] |
s |
True |
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示 |
[A-Z] |
B |
True |
[A-Z]就表示所有的大写字母 |
[0-9a-fA-F] |
e |
True |
可以匹配数字,大小写形式的a~f,用来验证十六进制字符 |
import re
"""re.findall(正则模式, 文本) 基于正则模式查找所有匹配的文本内容"""
# part1: 通配符->. 字符集-> []
ret1 = re.findall("a", "a,b,c,d,e")
ret1 = re.findall(".", "a,b,c,d,e")
ret1 = re.findall("[ace]", "a,b,c,d,e")
ret1 = re.findall("[a-z]", "a,b,c,d,e")
ret1 = re.findall("[0-9]", "1,2,3,4,5")
ret1 = re.findall("\d", "1,2,3,4,5")
ret1 = re.findall("[0-9a-z]", "1,a,2,b,3")
ret1 = re.findall("[^a-z]", "1,a,2,b,3")
ret1 = re.findall("[^0-9,]", "1,a,2,b,3")
print(ret1)
【3】元字符
元字符 |
匹配内容 |
. |
匹配除换行符以外的任意字符 |
\w |
匹配字母或数字或下划线 |
\s |
匹配任意的空白符 |
\d |
匹配数字 |
\n |
匹配一个换行符 |
\t |
匹配一个制表符 |
\b |
匹配一个单词的结尾 |
^ |
匹配字符串的开始 |
$ |
匹配字符串的结尾 |
\W |
匹配非字母或数字或下划线 |
\D |
匹配非数字 |
\S |
匹配非空白符 |
a|b |
匹配字符a或字符b |
() |
匹配括号内的表达式,也表示一个组 |
[...] |
匹配字符组中的字符 |
[^...] |
匹配除了字符组中字符的所有字符 |
# part2:重复元字符-> + * {} ?
ret2 = re.findall("[0-9a-zA-Z]", "apple,banana,orange,melon")
ret2 = re.findall("\w", "apple,banana,orange,melon")
ret2 = re.findall("\w+", "apple,banana,orange,melon")
ret2 = re.findall("\w+?", "apple,banana,orange,melon") # 取消贪婪匹配
ret2 = re.findall("\w*", "apple,banana,orange,melon")
ret2 = re.findall("\w{6}", "apple,banana,orange,melon")
【4】量词
量词 |
用法说明 |
* |
重复零次或更多次 |
+ |
重复一次或更多次 |
? |
重复零次或一次 |
{n} |
重复n次 |
{n,} |
重复n次或更多次 |
{n,m} |
重复n到m次 |
【5】位置元字符. ^ $
正则 |
待匹配字符 |
匹配 结果 |
说明 |
海. |
海燕海娇海东 |
海燕海娇海东 |
匹配所有"海."的字符 |
^海. |
海燕海娇海东 |
海燕 |
只从开头匹配"海." |
海.$ |
海燕海娇海东 |
海东 |
只匹配结尾的"海.$" |
# part3: 位置元字符-> ^ $
ret3 = re.findall("^\w{5}", "apple,banana,peach,orange,melon")
ret3 = re.findall("\w{5}$", "apple,banana,peach,orange,melon")
ret3 = re.findall("^\w{5}$", "apple,banana,peach,orange,melon")
print(ret3)
【6】重复匹配\* + ? { }
正则 |
待匹配字符 |
匹配 结果 |
说明 |
李.? |
李杰和李莲英和李二棍子 |
李杰 李莲 李二 |
?表示重复零次或一次,即只匹配"李"后面一个任意字符 |
李.* |
李杰和李莲英和李二棍子 |
李杰和李莲英和李二棍子 |
*表示重复零次或多次,即匹配"李"后面0或多个任意字符 |
李.+ |
李杰和李莲英和李二棍子 |
李杰和李莲英和李二棍子 |
+表示重复一次或多次,即只匹配"李"后面1个或多个任意字符 |
李.{1,2} |
李杰和李莲英和李二棍子 |
李杰和 李莲英 李二棍 |
{1,2}匹配1到2次任意字符 |
- 注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配
【7】字符集[][^]
正则 |
待匹配字符 |
匹配 结果 |
说明 |
李[杰莲英二棍子]* |
李杰和李莲英和李二棍子 |
李杰 李莲英 李二棍子 |
表示匹配"李"字后面[杰莲英二棍子]的字符任意次 |
李[^和]* |
李杰和李莲英和李二棍子 |
李杰 李莲英 李二棍子 |
表示匹配一个不是"和"的字符任意次 |
[\d] |
456bdha3 |
4 5 6 3 |
表示匹配任意一个数字,匹配到4个结果 |
[\d]+ |
456bdha3 |
456 3 |
表示匹配任意个数字,匹配到2个结果 |
【8】分组匹配 ()与 或 |[^]
- 身份证号码是一个长度为15或18个字符的字符串
- 如果是15位则全部🈶️数字组成,首位不能为0;
- 如果是18位,则前17位全部是数字,末位可能是数字或x,
- 下面我们尝试用正则来表示:
正则 |
待匹配字符 |
匹配 结果 |
说明 |
^[1-9]\d{13,16}[0-9x]$ |
110101198001017032 |
110101198001017032 |
表示可以匹配一个正确的身份证号 |
^[1-9]\d{13,16}[0-9x]$ |
1101011980010170 |
1101011980010170 |
表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字 |
^[1-9]\d{14}(\d{2}[0-9x])?$ |
1101011980010170 |
False |
现在不会匹配错误的身份证号了 ()表示分组,将\d{2}[0-9x]分成一组,就可以整体约束他们出现的次数为0-1次 |
`^([1-9]\d{16}[0-9x] |
[1-9]\d{14})$` |
110105199812067023 |
110105199812067023 |
# part4:
# | 指定原子或正则模式进行二选一或多选一
# () 具备模式捕获的能力,也就是优先提取数据的能力,通过(?:) 可以取消模式捕获
ret4 = re.findall(",\w{5},", ",apple,banana,peach,orange,melon,") # 筛选出5个字符的单词
ret4 = re.findall(",(\w{5}),", ",apple,banana,peach,orange,melon,") # 筛选出5个字符的单词
ret4 = re.findall("\w+@\w+\.com", "123abc@163.com,....234xyz@qq.com,....") # 筛选出5个字符的单词
ret4 = re.findall("(\w+)@qq\.com", "123abc@163.com,....234xyz@qq.com,....") # 筛选出5个字符的单词
ret4 = re.findall("(?:\w+)@(?:qq|163)\.com", "123abc@163.com,....234xyz@qq.com,....") # 筛选出5个字符的单词
print(ret4)
【9】转义符 \
- 在正则表达式中,有很多有特殊意义的是元字符
- 比如\n和\s等
- 如果要在正则中匹配正常的"\n"而不是"换行符"就需要对""进行转义,变成''。
- 在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。
- 所以如果匹配一次"\n",字符串中要写成'\n',那么正则里就要写成"\n",这样就太麻烦了。
- 这个时候我们就用到了r'\n'这个概念,此时的正则是r'\n'就可以了。
正则 |
待匹配字符 |
匹配 结果 |
说明 |
\n |
\n |
False |
因为在正则表达式中\是有特殊意义的字符,所以要匹配\n本身,用表达式\n无法匹配 |
\n |
\n |
True |
转义\之后变成\,即可匹配 |
"\n" |
'\n' |
True |
如果在python中,字符串中的''也需要转义,所以每一个字符串''又需要转义一次 |
r'\n' |
r'\n' |
True |
在字符串之前加r,让整个字符串不转义 |
# part5: 转义符-> \d \D \w \W \n \s \S \b \B
""" \b 1个单词边界原子 """
txt = "my name is nana. nihao,nana"
ret = re.findall(r"na", txt)
ret = re.findall(r"\bna", txt)
ret = re.findall(r"\bna\w{2}", txt)
print(ret) # ['na', 'na', 'na']
【10】贪婪匹配
- 贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配
正则 |
待匹配字符 |
匹配 结果 |
说明 |
<.*> |
<script>...<script> |
<script>...<script> |
默认为贪婪匹配模式,会匹配尽量长的字符串 |
<.*?> |
r'\d' |
<script> <script> |
加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串 |
- 几个常用的非贪婪匹配,Pattern
- *? 重复任意次,但尽可能少重复
- +? 重复1次或更多次,但尽可能少重复
- ?? 重复0次或1次,但尽可能少重复
- {n,m}? 重复n到m次,但尽可能少重复
- {n,}? 重复n次以上,但尽可能少重复
(11)
.
是任意字符
*
是取 0 至 无限长度
?
是非贪婪模式。
- 合在一起就是取尽量少的任意字符,一般不会这么单独写
- 他大多用在:
.*?
就是取前面任意长度的字符,直到一个x出现
【11】模式修正符
- 模式修正符,也叫正则修饰符,模式修正符就是给正则模式增强或增加功能的。
值 |
说明 |
re.I |
是匹配对大小写不敏感 |
re.L |
做本地化识别匹配 |
re.M |
多行匹配,影响到^和$ |
re.S |
使.匹配包括换行符在内的所有字符 |
re.U |
根据Unicode字符集解析字符,影响\w、\W、\b、\B |
re.X |
通过给予我们功能灵活的格式以便更好的理解正则表达式 |
import re
text = """
<12
>
<x
yz>
<!@#$%>
<1a!#
e2>
<>
"""
result_1 = re.findall("<.*?>", text)
result_2 = re.findall("<.*?>", text, re.S)
print(result_1) # ['<!@#$%>', '<>']
print(result_2) # ['<12\n>', '<x\n yz>', '<!@#$%>', '<1a!#\n e2>', '<>']
【二】正则模块(re)常用方法
【1】查找结果(findall)
import re
name_str = "Dream yuan Justin"
# 返回所有满足匹配条件的结果,放在列表里
result = re.findall('a', name_str)
print(result) # 结果 : ['a', 'a']
【2】查找结果(search)
import re
name_str = "Dream yuan Justin"
# 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
# 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
result = re.search('a', name_str).group()
print(result) # 结果 : 'a'
【3】查找结果(match)
import re
name_str = 'abc'
# 同search,不过尽在字符串开始处进行匹配
result = re.match('a', 'abc').group()
print(result)
# 结果 : 'a'
【4】切割(split)
import re
name_str = 'abcd'
# 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
result = re.split('[ab]', name_str)
print(result) # ['', '', 'cd']
【5】指定个数替换(sub)
import re
name_str = 'eva3egon4yuan4'
# 将数字替换成'H',参数1表示只替换1个
result = re.sub('\d', 'H', name_str, 1)
print(result) # evaHegon4yuan4
【6】替换全部(subn)
import re
name_str = 'eva3egon4yuan4'
# 将数字替换成'H',返回元组(替换的结果,替换了多少次)
result = re.subn('\d', 'H', 'eva3egon4yuan4')
print(result) # ('evaHegonHyuanH', 3)
【7】编译正则表达式(compile)
import re
# 将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
obj = re.compile('\d{3}')
# 正则表达式对象调用search,参数为待匹配的字符串
ret = obj.search('abc123eeee')
print(ret.group()) # 结果 : 123
【8】匹配结果为迭代器(finditer)
import re
# finditer返回一个存放匹配结果的迭代器
ret = re.finditer('\d', 'ds3sy4784a')
print(ret)
# <callable_iterator object at 0x10195f940>
print(next(ret).group())
# 查看第一个结果
print(next(ret).group())
# 查看第二个结果
print([i.group() for i in ret])
# 查看剩余的所有结果
【三】正则方法之优先级
【1】findall的优先级查询
import re
ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
# 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可
print(ret) # ['oldboy']
ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(ret) # ['www.oldboy.com']
【2】split的优先级查询
ret=re.split("\d+","eva3egon4yuan")
print(ret) #结果 : ['eva', 'egon', 'yuan']
ret=re.split("(\d+)","eva3egon4yuan")
print(ret) #结果 : ['eva', '3', 'egon', '4', 'yuan']
- 在匹配部分加上()之后所切出的结果是不同的,
- 没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
- 这个在某些需要保留匹配部分的使用过程是非常重要的。
【四】常用正则表达式
场景 |
正则表达式 |
用户名 |
^[a-z0-9_-]{3,16}$ |
密码 |
^[a-z0-9_-]{6,18}$ |
手机号码 |
^(?:\+86)?1[3-9]\d{9}$ |
颜色的十六进制值 |
^#?([a-f0-9] |
电子邮箱 |
^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+\.[a-z]+$ |
URL |
^(?:https:// |
IP 地址 |
((2[0-4]\d |
HTML 标签 |
^<([a-z]+)([^<]+)*(?:>(.*)<\/\1> |
utf-8编码下的汉字范围 |
^[\u2E80-\u9FFF]+$ |