正则表达式
正则表达式英文称regular expression
定义:正则表达式是一种文本模式匹配的工具,用于字符串的搜索,匹配和替换。在excel,word以及其他的文本编辑器都可直接适配。
一、基本匹配规则
- 
字面值字符:例如字母、数字、空格等,可以直接匹配它们自身。 
- 
特殊字符:例如点号 .、星号*、加号+、问号?等,它们具有特殊的含义和功能。
- 
字符类:用方括号 [ ]包围的字符集合,用于匹配方括号内的任意一个字符。
- 
元字符:例如 \d、\w、\s等,用于匹配特定类型的字符,如数字、字母、空白字符等。
- 
量词:例如 {n}、{n,}、{n,m}等,用于指定匹配的次数或范围。
- 
边界符号:例如 ^、$、\b、\B等,用于匹配字符串的开头、结尾或单词边界位置。
1.字符匹配
普通字符匹配:直接匹配文本中的字符,如abc表示直接匹配文本中包含为"abc"的字符
元字符匹配:元字符具有特殊的含义,例如 \d 匹配任意数字字符,\w 匹配任意字母数字字符,. 匹配任意字符(除了换行符)等。
常见的元字符如下:
| 元字符 | 含义 | 示例 | 
|---|---|---|
| . | 匹配任意单个字符(换行符除外) | a.c匹配 "abc"、"a1c" | 
| ^ | 匹配字符串开头 | ^abc匹配 "abc123" | 
| $ | 匹配字符串结尾 | abc$匹配 "123abc" | 
| * | 匹配前一个字符 0 次或多次 | a*匹配 "", "a", "aaa" | 
| + | 匹配前一个字符 1 次或多次 | a+匹配 "a", "aaa" | 
| ? | 匹配前一个字符 0 次或 1 次 | a?匹配 "", "a" | 
| {n} | 匹配前一个字符 n 次 | a{3}匹配 "aaa" | 
| {n,m} | 匹配前一个字符 n 到 m 次 | a{2,4}匹配 "aa", "aaa", "aaaa" | 
| [] | 匹配字符集合中的任意一个字符 | [abc]匹配 "a", "b", "c"[a-z]匹配小写字母 a 到 z 的任意一个[0-9]匹配数字 0 到 9 的任意一个[^...]匹配不在括号内的任意字符(字符集的否定) | 
| ` | ` | 或运算 | 
| \ | 转义字符,转义元字符或定义特殊字符 | \d、\s、\w,C:\\Users\\Qiu | 
| \d | 匹配任何数字,相当于 [0-9] | |
| \D | 匹配任何非数字字符,相当于 [^0-9] | |
| \w | 匹配任何单词字符(字母、数字、下划线),相当于 [a-zA-Z0-9_] | |
| \W | 匹配任何非单词字符,相当于 [^a-zA-Z0-9_] | |
| \s | 匹配任何空白字符(空格、制表符、换行符等) | |
| \S | 匹配任何非空白字符 | |
| () | 用于分组和捕获子表达式。 | (\d{4})-(\d{2}-\d{2})将\d{4}和\d{2}-\d{2}分为两组,可以对这两组进行捕获以及后期直接引用(\w{3})(\1)表示匹配连续两段的字母,如abbabb | 
| (?: ) | 用于分组但不捕获子表达式。 | 
2.量词匹配
- 
*:匹配前面的模式零次或多次。
- 
+:匹配前面的模式一次或多次。
- 
?:匹配前面的模式零次或一次。
- 
{n}:匹配前面的模式恰好 n 次。
- 
{n,}:匹配前面的模式至少 n 次。
- 
{n,m}:匹配前面的模式至少 n 次且不超过 m 次。
3.字符类
- 
[ ]:匹配括号内的任意一个字符。例如,[abc]匹配字符 "a"、"b" 或 "c"。
- 
[^ ]:匹配除了括号内的字符以外的任意一个字符。例如,[^abc]匹配除了字符 "a"、"b" 或 "c" 以外的任意字符。
4.边界匹配(定位符)
定位符用来描述字符串或单词的边界,^ 和 $ 分别指字符串的开始与结束,\b 描述单词的前或后边界,\B 表示非单词边界。
- 
^:匹配字符串的开头。
- 
$:匹配字符串的结尾。
- 
\b:匹配单词边界。
- 
\B:匹配非单词边界。
5.分组和捕获
- 
( ):用于分组和捕获子表达式。
- 
(?: ):用于分组但不捕获子表达式。
6.特殊字符
- 
\:转义字符,用于匹配特殊字符本身。
- 
.:匹配任意字符(除了换行符)。
- 
|:用于指定多个模式的选择。
7.分组与引用
() 表示分组,匹配子表达式并捕获;() 会把每个分组里的匹配的值保存起来, 多个匹配值可以通过数字 n 来查看(n 是一个数字,表示第 n 个捕获组的内容)。如:(\w{3})(\1) 表示匹配连续两段的字母,如abbabb
- (\d{4})-(\d{2}-\d{2})将- \d{4}和- \d{2}-\d{2}分为两组,可以对这两组进行捕获以及后期直接引用
但用圆括号会有一个副作用,使相关的匹配会被缓存,此时可用 ?: 放在第一个选项前来消除这种副作用。即?: 仅于分组但不捕获子表达式。
其中?:为非捕获元之一,非捕获元表示不会将表达式内容包含在结果当中。同等的非捕获元还有?= 和 ?!
8.正、负向零宽断言
?=:正向零宽断言 (Positive Lookahead)
- 
exp1(?=exp2)查找在exp2前面的exp1,且exp2不会包含在结果当中
- 
(?<=exp2)exp1表示查找在exp2前面的exp1,且exp2不会包含在结果当中
            
            
              python
              
              
            
          
          import re
 
 pattern = r"I love (?=python)" # 匹配 "python" 前面带有 "I love " 的部分
 text = "I love python, I love coding."
 
 match = re.search(pattern, text)
 if match:
     print(match.group())  # 输出:I love
?!:负向零宽断言 (Negative Lookahead)
- 
exp1(?!exp2)查找在exp2后面不跟 的exp1,且exp2不会包含在结果当中。
- 
(?<!exp2)exp1查找在exp2前面不跟 的exp1,且exp2不会包含在结果当中。
            
            
              python
              
              
            
          
           import re
 
 pattern = r"I love (?!python)"
 text = "I love coding, I love python." # 匹配不以 "python" 结尾的 "I love "
 
 matches = re.findall(pattern, text)
 print(matches)  # 输出:['I love '] ?= 和 ?!的区别:
| 特性 | ?=(正向断言) | ?!(负向断言) | 
|---|---|---|
| 断言结果 | 后面 必须是 pattern | 后面 不能是 pattern | 
| 是否消耗字符 | 否 | 否 | 
| 适用场景 | 验证后续内容符合预期 | 排除后续内容不符合预期 | 
9.贪婪与非贪婪匹配
* 和 + 限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配。
**贪婪匹配:**尽可能多地匹配。
- 示例:a.*b匹配 "a123b456b" 中的 "a123b456b"。
**非贪婪匹配:**尽可能少地匹配。
- 
语法:在元字符后加 ?。
- 
示例: a.*?b匹配 "a123b456b" 中的 "a123b"。
案例:如<.*>会匹配到如下html标签内的所有值

但是如果我们只需要匹配一个<h1>标签,我们可以使用非贪婪匹配:<.*?>

通过在 * 、+ 或 ? 限定符之后放置 ?,该表达式从"贪婪"表达式转换为"非贪婪"表达式或者最小匹配。
10.标记/修饰符(flag)
| 符号 | 作用 | 示例 | 
|---|---|---|
| i | ignore - 不区分大小写 | 将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别。 | 
| g | global - 全局匹配 | 查找所有的匹配项。 | 
| m | multi line - 多行匹配 | 使边界字符 ^ 和 $ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾。 | 
| s | 特殊字符圆点 . 中包含换行符 \n | 默认情况下的圆点 . 是匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n。 | 
- i表示不区分大小写:

- m表示多行匹配(每行都进行匹配)

- 默认情况下的圆点 . 是 匹配除换行符 \n 之外的任何字符,加上 s 之后, . 中包含换行符 \n。

python re库内的正则表达式支持的修饰符(可选标志)

re.IGNORECASE (re.I):忽略大小写。
re.MULTILINE (re.M):多行模式,^ 和 $ 会匹配每一行的开头和结尾。
re.DOTALL (re.S):单行模式,使 . 匹配换行符。
re.VERBOSE (re.X):允许在正则表达式中添加注释和多行书写。
其他标志如 re.ASCII、re.LOCALE 等。
二、Python正则表达式API使用
Python 提供了 re 模块用于操作正则表达式。

| 函数 | 含义 | 
|---|---|
| re.match | 从字符串开头匹配,返回 Match对象 | 
| re.search | 在字符串中搜索第一个匹配 | 
| re.findall | 返回所有匹配结果的列表 | 
| re.finditer | 返回所有匹配结果的迭代器 | 
| re.sub | 替换匹配到的内容 | 
| re.split | 按正则表达式分割字符串 | 
1.查找
| 方法 | 作用描述 | 
|---|---|
| re.match | 从字符串的开头尝试匹配正则表达式,只返回第一个匹配的对象。 | 
| re.search | 搜索整个字符串,返回第一个匹配的对象。 | 
| re.findall | 搜索所有符合正则表达式的内容,返回匹配结果的列表。 | 
| re.finditer | 搜索所有符合正则表达式的内容,返回一个迭代器(每项是 Match对象)。 | 
1.re.match/re.search
re.match 只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None,而 re.search 匹配整个字符串,直到找到一个匹配。
 re.match(pattern, string, flags=0) # pattern正则表达式;string需要匹配的字符;flag修饰符模式。match匹配字符串的开始,如果字符串开始不符合正则表达式,则返回None,否则返回一个Match对象
 re.search(pattern, string, flags=0) # re.search 匹配整个字符串,匹配成功返回一个Match对象,否则返回NoneMatch对象方法:
- 
group(num=0)当查找字符串分组时,group(num) 将返回对应num组的元组(1开始分组),group(0)/group(num)表示全部匹配字符串。
- 
groups()返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。
 
            
            
              python
              
              
            
          
          import re
 
 line = "Cats are smarter than dogs"
 
 # 匹配字符串的开始,如果开始不成功,则匹配失败
 matchObj = re.match(r"dogs", line, re.M | re.I)  # return:None
 if matchObj:
     print(matchObj.group())
 else:
     print("No match!!") # No match!!
 
 # 匹配整个字符串直到找到至少一个匹配
 matchObj = re.search(r"dogs", line, re.M | re.I)  # return:dogs
 if matchObj:
     print(matchObj.group()) # dogs
 else:
     print("No match!!")
     
 分组
 text2 = "18812345678,他还有一个电话号码是18887654321,他爱好的数字是01234567891,他的座机是:0571-52152166,0571-52152177"
 m1 = re.search(
     r"(\d{4})-(\d{8})", text2
 )  # 返回一个Match对象,调用Match对象的group方法获取返回值
 print(m1.group())  # 0571-52152166
 print(m1.group(1))  # 0571,取表达式的第一组匹配值(\d{4})
 print(m1.group(2))  # 52152166,取表达式的第二组匹配值(\d{8})2.findall
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。
 re.findall(pattern, string, flags=0)
 或
 pattern.findall(string[, pos[, endpos]])- 
pattern 匹配模式。 
- 
string 待匹配的字符串。 
- 
pos 可选参数,指定字符串的起始位置,默认为 0。 
- 
endpos 可选参数,指定字符串的结束位置,默认为字符串的长度。 
            
            
              python
              
              
            
          
          import re
  
 result1 = re.findall(r'\d+','runoob 123 google 456')
  
 pattern = re.compile(r'\d+')   # 查找数字
 result2 = pattern.findall('runoob 123 google 456')
 result3 = pattern.findall('run88oob123google456', 0, 10)
  
 print(result1) # ['123', '456']
 print(result2) # ['123', '456']
 print(result3) # ['88', '12']
 
 多个匹配模式,返回元组列表
 import re
 
 result = re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
 print(result) # [('width', '20'), ('height', '10')]3.re.finditer
和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
            
            
              python
              
              
            
          
          re.finditer(pattern, string, flags=0)
 import re
  
 it = re.finditer(r"\d+","12a32bc43jf3") 
 for match in it: 
     print(match.group())
 12 
 32 
 43 
 3findall和finditer的区别
- 
findall返回的可能是元组列表,也可能是字符串列表,取决于正则表达式内是否分组
- 
finditer返回的是Match对象的迭代器
2.替换
替换功能可以用正则表达式匹配到的部分内容进行替换,主要通过 re.sub 和 re.subn 方法实现。
| 方法 | 作用描述 | 
|---|---|
| re.sub | 替换匹配到的内容,返回替换后的新字符串。 | 
| re.subn | 替换匹配到的内容,返回元组 (新字符串, 替换次数)。 | 
- 
sub:返回替换后的字符串。
- 
subn:返回包含替换后字符串和替换次数的元组。
1.re.sub&re.subn
 re.sub(pattern, repl, string, count=0, flags=0) # 前三个为必选参数,后两个为可选参数。- 
pattern : 正则中的模式字符串。 
- 
repl : 替换的字符串,也可为一个函数。 
- 
string : 要被查找替换的原始字符串。 
- 
count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。 
- 
flags : 编译时用的匹配模式,数字形式。 
 
            
            
              python
              
              
            
          
          import re
  
 phone = "2004-959-559 # 这是一个电话号码"
  
 # 删除注释
 num = re.sub(r'#.*$', "", phone)
 print ("电话号码 : ", num)
  
 # 移除非数字的内容
 num = re.sub(r'\D', "", phone)
 print ("电话号码 : ", num)
 # 当repl 参数是一个函数
 import re
  
 # 将匹配的数字乘以 2
 def double(matched):
     value = int(matched.group('value'))
     return str(value * 2)
  
 s = 'A23G4HFD567'
 print(re.sub('(?P<value>\d+)', double, s)) # A46G8HFD1134subn和sub用法一样,只是subn会额外将替换的次数一起封装成元组返回
            
            
              python
              
              
            
          
          text = "abc,adfasda,Abc,ABC"
 sub = re.sub(r"(abc)", "***", text, flags=re.I)
 subn = re.subn(r"(abc)", "***", text, flags=re.I)
 print(sub)  # re.sub返回替换后的字符串,***,adfasda,***,***
 print(subn)  # re.subn返回替换后的字符串和替换次数的元组,('***,adfasda,***,***', 3)3.分割split
 re.split(pattern, string[, maxsplit=0, flags=0])- 
pattern正则表达式
- 
string需要匹配的字符串
- 
maxsplit分割次数,maxsplit=1 分割一次,默认为 0表示不限制次数。
- 
flags标志位
            
            
              python
              
              
            
          
           text6 = "abc,  ddd? xxx. fff! asd"
 split = re.split(
     r"\s*[,?!.]\s*", text6
 )  # 将【,?!.】符号以及旁边的空格作为分隔符分割字符串,返回一个字符串数组
 print(split)  # ['abc', 'ddd', 'xxx', 'fff', 'asd']
 
 re.split('a*', 'hello world')   # 对于一个找不到匹配的字符串而言,split 不会对其作出分割
 # ['hello world']4.compile
compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。语法格式为:
 re.compile(pattern[, flags])- 
pattern : 一个字符串形式的正则表达式 
- 
flags 可选,表示匹配模式,比如忽略大小写,多行模式等 
- 
返回 RegexObject对象,该对象可以调用匹配方法(如match、search、findall等)。
            
            
              python
              
              
            
          
           # 编译正则表达式
 pattern = re.compile(r"\d+")
 
 # compile
 # 使用编译的正则对象进行匹配和查找
 result1 = pattern.findall("There are 3 cats and 4 dogs.")
 result2 = pattern.findall("There are 6 cats and 8 dogs.")
 print(result1)  # 输出 ['3', '4']
 print(result2)  # 输出 ['6', '8']re.compile() 是处理复杂正则表达式的最佳选择,尤其是需要频繁使用相同的正则时。它能提高代码效率、增强可读性,是 Python 正则表达式模块中非常重要的功能。