正则

发布时间 2023-12-28 11:57:59作者: 苏苏!!

(一)正则语法

(1)引入

  • 一说规则我已经知道你很晕了
  • 首先你要知道的是
    • 谈到正则,就只和字符串相关了。
    • 在我给你提供的工具中,你输入的每一个字都是一个字符串。
  • 其次,如果在一个位置的一个值,不会出现什么变化,那么是不需要规则的。
    • 比如
      • 你要用"1"去匹配"1"
      • 或者用"2"去匹配"2"
      • 直接就可以匹配上。
    • 这连python的字符串操作都可以轻松做到。
    • 那么在之后我们更多要考虑的是在同一个位置上可以出现的字符的范围。

(2)字符组

  • 字符组 :[字符组] 在同一个位置可能出现的各种字符组成了一个字符组
  • 在正则表达式中用[]表示
  • 字符分为很多类
    • 比如数字、字母、标点等等。
  • 假如你现在要求一个位置"只能出现一个数字"
  • 那么这个位置上的字符只能是0、1、2...9这10个数之一。
正则 待匹配字符 匹配结果 说明
[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>', '<>']

正则语法基础

"""正则语法基础"""
# (一)字符组[...]---一个字符
# 字符组就是在同一个位置出现的各种字符的组合,只能匹配一个字符
# (1)匹配数字[0123..9]---[0-9]
# (2)匹配小写字母[abcd...z]---[a-z]
# (3)匹配大写字母[ABC...Z]---[A-Z]
# (4)匹配任意数字或小写字母或大写字母[0-9a-zA-Z]

# (二)元字符--- 一个字符
# (1). 匹配任意一个字符,但是不能匹配换行符
# (2)\w 匹配任意一个数字、字母或下划线
# (3)\d 匹配任意一个数字
# (4)\s 匹配任意的空白符(其实就是空格)
# (5)\n 匹配换行符
# (6)\t 匹配制表符
# (7)^ 匹配字符串的开始
# (8)$ 匹配字符串的结尾
# (9)\D 匹配任意非数字
# (10)\S 匹配任意非空白格
# (11)\W 匹配非数字、非字母、非下划线
# (12)a|b 匹配前面或者后面的字符
# (13)[...] 匹配字符组中的任意一个字符
# (14)[^...] 匹配非字符组中的任意一个字符

# (三)量词
# 控制字符重复的次数,默认是贪婪匹配,就是默认是最多次
# (1)*:重复零次或者更多次
# (2)+:重复一次或者更多次
# (3)?:重复零次或者一次
# (4){n}:重复n次
# (5){n,}:重复n次或者更多次
# (6){n,m}:最少重复n次,最多重复m次

# (四)转义符
# (1)\n:换行符
# (2)r'\n':取消转义
# (3)'\\n':取消转义符的转义功能

# (五)取消贪婪匹配
# *? 贪婪匹配字符 +? 取消贪婪匹配的含义

# (六)练习
# 【4】做练习--字符组+元字符
# 海.   海燕海椒海东王  海燕海椒海东
# ^海.  海燕海椒海东王  海燕
# 海.$  海燕海椒海东王  没东西

# 【5】做练习 -- 字符组 + 量词练习
# 李.? 李杰和李莲英和李二棍子 李杰 李莲 李二
# 李.* 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子
# 李.+ 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子
# 李.{1,2} 李杰和李莲英和李二棍子  李杰和 李莲英 李二棍
# 李.{1,} 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子

# 【6】做练习 -- 字符组 + 字符组
# 李[杰莲英二棍子] 李杰和李莲英和李二棍子  李杰 李莲 李二
# 李[^和]* 李杰和李莲英和李二棍子 李杰 李莲英 李二棍子
# [\d] 456dbh3  4 5 6 3
# [\d]+ 456dbh3 456 3

# 【7】分组匹配
# ^[1-9]\d{13,16}[0-9x]$ 11010119800101703452
# ^[1-9] ^ 以[1-9]开头
# [^1-9] ^ 不能是[1-9]

# [0-9x]$
# 以[1-9]开头 + 任意数字重复 13 - 16次 + 必须以 0-9或x结尾

# ^[1-9]\d{14}(\d{2}[0-9x])?$ 110101198001017034

# ? : 重复零次或一次
# 以[1-9]开头 + 14个数字 + 必须以 (优先匹配 2个数字 + 0-9或x)重复0次或1次 结尾

re模块

import re

# #1.查找模块
# info='my name is syh ,my age is 23'
# #findall(pattern正则表达式,string待匹配的内容)
# #在待查找内容中按照正则表达式将正则表达式需要匹配到的内容匹配出来
# res=re.findall(pattern='\d',string=info)
# print(res)#['2', '3']
#
# #2.编译正则表达式
# re_type=re.compile(pattern='\w')
# print(re_type)#re.compile('\\d')
# re_one=re.findall(pattern=re_type,string=info)
# print(re_one)
# #['m', 'y', 'n', 'a', 'm', 'e', 'i', 's', 's', 'y', 'h', 'm', 'y', 'a', 'g', 'e', 'i', 's', '2', '3']
#
# #3.查找结果
# re_two=re.search(pattern=re_type,string=info)
# print(re_two)
# # <re.Match object; span=(0, 1), match='m'>
# re_three=re.search(pattern=re_type,string=info).group()
# print(re_three)#m
# re_three=re.search(pattern=re_type,string=info).groups()
# print(re_three)#()

# #4.查找结果match
# info='my name is syh ,my age is 23'
# re_type=re.compile(pattern='\w')
# re_one=re.match(pattern=re_type,string=info)
# print(re_one)
# #<re.Match object; span=(0, 1), match='m'>
# re_two=re.match(pattern=re_type,string=info).group()
# print(re_two)#m

#5.切割split
# res='12345'
# res1=re.split(pattern='3',string=res)
# print(res1)#['12', '45']

#6.指定个数替换
# 1.re.subn(pattern=正则表达式,repl=替换进去的内容,string=待匹配的内容,count=修改字符的个数)
# 返回值是一个元祖,元组的第 0 个元素是影响后的结果 , 第 1 个元素是影响字符的个数

# info='my name is syh ,my age is 23'
# res=re.subn(pattern='\d',repl='20',string=info,count=2)
# print(res)#('my name is syh ,my age is 2020', 2)

# # 2.re.sub(pattern=正则表达式,repl=替换进去的内容,string=待匹配的内容,count=修改字符的个数)
# # 返回值是修改完成后的影响的结果
# info='my name is syh ,my age is 23'
# res=re.sub(pattern='\d',repl='20',string=info,count=1)
# print(res)#my name is syh ,my age is 203
# res=re.sub(pattern='\d',repl='20',string=info,count=2)
# print(res)#my name is syh ,my age is 2020

# #7.匹配结果为迭代器
# res='abc123abc'
# res1=re.finditer(pattern='\d*',string=res)
# print(res1)#<callable_iterator object at 0x0000021A91F5B0D0>
# 
# for i in res1:
#     print(i)
# 
# # <re.Match object; span=(0, 0), match=''>
# # <re.Match object; span=(1, 1), match=''>
# # <re.Match object; span=(2, 2), match=''>
# # <re.Match object; span=(3, 6), match='123'>
# # <re.Match object; span=(6, 6), match=''>
# # <re.Match object; span=(7, 7), match=''>
# # <re.Match object; span=(8, 8), match=''>
# # <re.Match object; span=(9, 9), match=''>

re模块优先级

"""re模块的优先级"""
## # 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可
res = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
print(res)  # ['oldboy']

res = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(res)  # ['www.oldboy.com']

# 待匹配字符串
start_str = '<span class="content-text" data-count="414" data-index="2">  早上七点半,昏暗的房间里,松软的大床上,张元清陡然惊醒,捂着头,弓身如虾。</span>'
res = re.compile(pattern=r'<span class="content-text" data-count="414" data-index="2">(.*)</span>')
res = re.findall(pattern=res, string=start_str)
print(res)  # ['\u3000\u3000早上七点半,昏暗的房间里,松软的大床上,张元清陡然惊醒,捂着头,弓身如虾。']