- 正则表达式
正则表达式用于查找符合规则的字符串。正则表达式具备通用性,所以是用处最广泛的字符串匹配语法,无论是Python、Java、PHP,还是数据库语言SQL,它的语法规则是基本通用的。
比如:
'哈哈哈哈哈哈我爱你' → 匹配 '我爱你'
Python中如何使用正则表达式
- 导入模块
- import re # Python标准库
- 使用方法进行匹配
例如:match()方法
- result = re.match('正则表达式', '目标字符串')
- 获得匹配结果
- print(result.group()) # 获取匹配结果
基本使用示例:
- # 1.导入模块
- import re
- # 2.使用方法进行匹配操作,例如match(从开始位置往后查找匹配)
- result = re.match(r'.*我爱你', '哈哈哈哈哈哈我爱你')
- '''其中��
- r'.*我爱你':是正则表达式
- '哈哈哈哈哈哈我爱你':是要匹配的字符串
- '''
- # 3、拿到匹配的结果数据,可以通过group方法获取
- print(result.group())
- 匹配规则
匹配单个字符
表格:
符号 |
功能 |
. |
匹配任意字符(除\n) |
[] |
匹配括号内任意字符(比如[abc]) |
\d |
匹配数字(0-9) |
\D |
匹配非数字 |
\s |
匹配空白(空格、\t、\n) |
\S |
匹配非空白 |
\w |
匹配单词字符(a-zA-Z0-9_汉字) |
\W |
匹配非单词字符 |
- .匹配任意字符(除换行符 \n)
- import re
- # 示例1:匹配任意字符(字母)
- ret = re.match(".", "A")
- print(ret.group()) # 输出: A
- # 示例2:匹配任意字符(数字)
- ret = re.match(".", "7")
- print(ret.group()) # 输出: 7
- # 示例3:匹配任意字符(符号)
- ret = re.match(".", "$")
- print(ret.group()) # 输出: $
- # 示例4:无法匹配换行符
- ret = re.match(".", "\n") # 匹配失败
- print(ret) # 输出: None
2. []:匹配括号内任意一个字符
- import re
- # 示例1:匹配指定字符 [abc]
- ret = re.match("[abc]", "b") # 匹配成功
- print(ret.group()) # 输出: b
- # 示例2:匹配范围字符 [0-9]
- ret = re.match("[0-9]", "5") # 匹配数字
- print(ret.group()) # 输出: 5
- # 示例3:匹配混合范围 [a-zA-Z]
- ret = re.match("[a-zA-Z]", "Z") # 匹配大写字母
- print(ret.group()) # 输出: Z
- # 示例4:匹配失败
- ret = re.match("[abc]", "d") # 匹配失败
- print(ret) # 输出: None
3. \d:匹配数字(0-9)
- import re
- # 示例1:匹配数字
- ret = re.match("\d", "9") # 匹配成功
- print(ret.group()) # 输出: 9
- # 示例2:匹配失败(非数字)
- ret = re.match("\d", "a") # 匹配失败
- print(ret) # 输出: None
4. \D:匹配非数字
- # 示例1:匹配字母
- ret = re.match("\D", "A") # 匹配成功
- print(ret.group()) # 输出: A
- # 示例2:匹配符号
- ret = re.match("\D", "$") # 匹配成功
- print(ret.group()) # 输出: $
- # 示例3:匹配失败(数字)
- ret = re.match("\D", "5") # 匹配失败
- print(ret) # 输出: None
5. \s:匹配空白字符(空格、\t、\n)
- import re
- # 示例1:匹配空格
- ret = re.match("\s", " ") # 匹配成功
- print(repr(ret.group())) # 输出: ' '
- # 示例2:匹配制表符 `\t`
- ret = re.match("\s", "\t") # 匹配成功
- print(repr(ret.group())) # 输出: '\t'
- # 示例3:匹配换行符 `\n`
- ret = re.match("\s", "\n") # 匹配成功
- print(repr(ret.group())) # 输出: '\n'
- # 示例4:匹配失败(非空白)
- ret = re.match("\s", "a") # 匹配失败
- print(ret) # 输出: None
6. \S:匹配非空白字符
- import re
- # 示例1:匹配字母
- ret = re.match("\S", "A") # 匹配成功
- print(ret.group()) # 输出: A
- # 示例2:匹配数字
- ret = re.match("\S", "5") # 匹配成功
- print(ret.group()) # 输出: 5
- # 示例3:匹配失败(空白符)
- ret = re.match("\S", "\t") # 匹配失败
- print(ret) # 输出: None
7. \w:匹配单词字符(字母、数字、下划线、汉字)
- import re
- '''7. \w:匹配单词字符(字母、数字、下划线、汉字)'''
- # 示例1:匹配字母
- ret = re.match("\w", "a") # 匹配成功
- print(ret.group()) # 输出: a
- # 示例2:匹配数字
- ret = re.match("\w", "7") # 匹配成功
- print(ret.group()) # 输出: 7
- # 示例3:匹配下划线
- ret = re.match("\w", "_") # 匹配成功
- print(ret.group()) # 输出: _
- # 示例4:匹配汉字
- ret = re.match("\w", "周") # 匹配成功
- print(ret.group()) # 输出: 周
8. \W:匹配非单词字符(符号、空格等)
- import re
- # 示例1:匹配符号
- ret = re.match("\W", "$") # 匹配成功
- print(ret.group()) # 输出: $
- # 示例2:匹配空格
- ret = re.match("\W", " ") # 匹配成功
- print(repr(ret.group())) # 输出: ' '
- # 示例3:匹配失败(单词字符)
- ret = re.match("\W", "a") # 匹配失败
- print(ret) # 输出: None
匹配多个字符
符号 |
功能 |
* |
匹配前一个字符0次或多次(贪婪模式) |
+ |
匹配前一个字符1次或多次 |
? |
匹配前一个字符0次或1次 |
{m} |
精确匹配m次 |
{m,n} |
匹配m到n次 |
1. *:匹配前一个字符 0次或多次(贪婪模式)
- import re
- # 示例1:匹配小写字母0次或多次(允许大写后无小写)
- ret = re.match("[A-Z][a-z]*", "M") # 匹配成功(0次小写)
- print(ret.group()) # 输出: M
- # 示例2:匹配多个小写字母
- ret = re.match("[A-Z][a-z]*", "MdueD6Ddas") # 匹配到第一个非小写字母前
- print(ret.group()) # 输出: Mdue
- # 示例3:匹配数字0次或多次
- ret = re.match("\d*", "abc") # 匹配空字符串(0次数字)
- print(ret.group()) # 输出: (空字符串,无报错)
2. +:匹配前一个字符 1次或多次(至少一次)
- # 示例1:匹配小写字母至少一次
- ret = re.match("[A-Z][a-z]+", "Hello") # 匹配成功
- print(ret.group()) # 输出: Hello
- # 示例2:匹配失败(小写字母不足一次)
- ret = re.match("[A-Z][a-z]+", "H") # 匹配失败
- print(ret) # 输出: None
- # 示例3:匹配连续数字
- ret = re.match("\d+", "138abc") # 匹配到第一个非数字前
- print(ret.group()) # 输出: 138
3. ?:匹配前一个字符 0次或1次(可选匹配)
- # 示例1:匹配可选字符(http或https)
- ret = re.match("https?", "http") # 匹配成功(0次s)
- print(ret.group()) # 输出: http
- ret = re.match("https?", "https") # 匹配成功(1次s)
- print(ret.group()) # 输出: https
- # 示例2:匹配可选数字
- ret = re.match("周周\d?", "周周") # 匹配成功(0次数字)
- print(ret.group()) # 输出: 周周
- ret = re.match("周周\d?", "周周5") # 匹配成功(1次数字)
- print(ret.group()) # 输出: 周周5
4. {m}:精确匹配前一个字符 m次
- # 示例1:精确匹配11位手机号
- ret = re.match("\d{11}", "13847758302") # 匹配成功
- print(ret.group()) # 输出: 13847758302
- # 示例2:匹配失败(不足m次)
- ret = re.match("\d{3}", "12") # 匹配失败
- print(ret) # 输出: None
5.{m,n}:匹配前一个字符 m到n次(贪婪模式)
- # 示例1:匹配6~10位数字
- ret = re.match("\d{6,10}", "1384775830245678") # 匹配前10位
- print(ret.group()) # 输出: 1384775830
- # 示例2:匹配失败(不足m次)
- ret = re.match("\d{6,10}", "13567") # 匹配失败(仅5位)
- print(ret) # 输出: None
匹配开头结尾
符号 |
功能 |
^ |
匹配字符串开头 |
$ |
匹配字符串结尾 |
- ^:匹配字符串开头
强制匹配从字符串的第一个字符开始。
- import re
- # 示例:匹配以大写字母开头的字符串
- text = "Hello"
- pattern = "^[A-Z]" # 从开头匹配大写字母
- ret = re.match(pattern, text)
- print(ret.group()) # 输出: H
- # 匹配失败示例
- text = "hello"
- ret = re.match(pattern, text)
- print(ret) # 输出: None
2. $:匹配字符串结尾
\d$ 匹配结尾数字:
- # 示例:匹配以数字结尾的字符串
- text = "user123"
- pattern = ".*\d$" # 开头任意内容 + 结尾数字
- ret = re.match(pattern, text)
- print(ret.group()) # 输出: user123
- # 匹配失败示例
- text = "user123a"
- ret = re.match(pattern, text)
- print(ret) # 输出: None
3. ^ 和 $ 联合使用:精确匹配整个字符串
确保字符串完全符合指定模式。
- # 示例1:校验手机号(必须为11位纯数字)
- ret = re.match("^\d{11}$", "13847758302") # 匹配成功
- print(ret.group()) # 输出: 13847758302
- # 示例2:校验邮箱格式(简单示例)
- email_regex = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
- ret = re.match(email_regex, "user@example.com") # 匹配成功
- print(ret.group()) # 输出: user@example.com
- # 示例3:匹配失败(字符串包含多余字符)
- ret = re.match("^abc$", "abc123") # 结尾不是abc
- print(ret) # 输出: None
4. 转义字符:匹配字面意义的 ^ 或 $
使用 \ 转义符号本身。(这里用的方法是search(),后面有对该方法的使用详解。)
- # 示例1:匹配以 $ 开头的字符串
- ret = re.search("^\$", "$100") # 匹配成功
- print(ret.group()) # 输出: $
- # 示例2:匹配包含 ^ 的字符串
- ret = re.search("\^", "a^b") # 匹配成功
- print(ret.group()) # 输出: ^
匹配分组
符号 |
功能 |
| |
匹配左右任意一个表达式 |
(ab) |
将括号中字符作为一个分组 |
\num |
引用分组num匹配到的字符串 |
(?P<name>) |
分组起别名 |
(?P=name) |
引用别名为name分组匹配到的字符串 |
1. |:匹配左右任意一个表达式
用于逻辑“或”操作,通常在分组内定义多个候选模式。
- import re
- # 示例:匹配纯小写、纯大写或纯数字的字符串(完全匹配)
- lyst = ['abc', 'ABC', '123', 'abc123', 'ABC123', '哈哈哈哈哈哈']
- for text in lyst:
- # 正则说明:匹配纯小写 | 纯大写 | 纯数字(必须从开头到结尾)
- pattern = r"^([a-z]+|[A-Z]+|\d+)$"
- match = re.match(pattern, text)
- if match:
- print(f"'{text}' 匹配成功 → 分组内容: {match.group(1)}")
- else:
- print(f"'{text}' 匹配失败")
- # 输出:
- # 'abc' 匹配成功 → 分组内容: abc
- # 'ABC' 匹配成功 → 分组内容: ABC
- # '123' 匹配成功 → 分组内容: 123
- # 'abc123' 匹配失败
- # 'ABC123' 匹配失败
- # '哈哈哈哈哈哈' 匹配失败
2. (ab) 与 \num
(ab):创建捕获分组。将括号内内容作为一个分组,后续可通过索引提取。
\num:引用分组。通过 \1、\2 等格式引用前面分组的匹配结果(索引从 1 开始)。
- # 示例:匹配重复的分组内容(如 "abcabc")
- text = "abcabc"
- pattern = r"^([a-z]{3})\1$" # \1 引用第一个分组(匹配 "abc" 重复一次)
- match = re.match(pattern, text)
- print(match.group()) # 输出: abcabc
- # 示例:匹配重复的标签结构(如 "<html>content</html>")
- text = "<html>content</html>"
- pattern = r"^<(\w+)>.*</\1>$" # \1 引用分组1(标签名)
- match = re.match(pattern, text)
- print(match.group()) # 输出: <html>content</html>
3. (?P<name>) 与 (?P=name)
(?P<name>):命名分组。为分组指定别名,通过名称提取内容(提高可读性)。
(?P=name):引用命名分组。通过别名引用之前的分组匹配结果。
- # 示例1:匹配 HTML 标签结构(确保标签名一致)
- text = "<html><h1>www.baidu.com</h1></html>"
- pattern = r"^<(?P<tag_html>html)><(?P<tag_h1>h1)>.*</(?P=tag_h1)></(?P=tag_html)>$"
- match = re.match(pattern, text)
- if match:
- print("匹配成功:", match.group())
- print("HTML标签名:", match.group("tag_html")) # 输出: html
- print("H1标签名:", match.group("tag_h1")) # 输出: h1
- else:
- print("匹配失败")
- # 示例2:匹配重复的字母+数字组合(如 "abc123abc123")
- text = "abcdef0123456789abcdef0123456789"
- pattern = r"^(?P<letters>[a-z]+)(?P<numbers>\d+)(?P=letters)(?P=numbers)$"
- match = re.match(pattern, text)
- if match:
- print("匹配成功:", match.group())
- print("字母部分:", match.group("letters")) # 输出: abcdef
- print("数字部分:", match.group("numbers")) # 输出: 0123456789
- else:
- print("匹配失败")
- re模块的其它匹配用法。
上面的正则匹配规则中,多数用到的都是match方法,其实re模块中还要很多其它的方法。下面举几个常用的用法一一讲解。
1. re.match(pattern, string)
- 功能:从字符串开头匹配正则表达式,若开头不匹配则返回 None。
- 参数说明:
- pattern:正则表达式(字符串或预编译对象)。
- string:要匹配的目标字符串。
- 返回值:Match 对象(匹配成功)或 None。
- 示例:
- import re
- # 示例:匹配以大写字母开头的单词
- text = "Hello World"
- match = re.match(r"^[A-Z]\w+", text)
- if match:
- print(match.group())
- else:
- print('不匹配')
2. re.search(pattern, string)
- 功能:扫描整个字符串,返回第一个匹配项。
- 参数说明:
- pattern:正则表达式。
- string:目标字符串。
- 返回值:Match 对象或 None。
- 示例:
- # 示例:查找字符串中的第一个数字
- match_obj = re.search("\d+", "周周已经30岁了,蓓蓓才10岁")
- if match_obj:
- # 获取匹配结果数据
- print(match_obj.group())
- else:
- print("匹配失败")
3. re.findall(pattern, string)
- 功能:返回所有非重叠匹配项的列表。
- 参数说明:
- pattern:正则表达式。
- string:目标字符串。
- 返回值:
- 无分组时:字符串列表(如 ['a', 'b'])。
- 有分组时:元组列表(如 [('2023', '10'), ('2024', '01')])。
- 示例:
- match_obj = re.findall("\d+", "周周已经30岁了,蓓蓓才10岁")
- if match_obj:
- # 获取匹配结果数据
- print(match_obj)
- else:
- print("匹配失败")
分组对匹配结果的影响:
- # ------------ 示例1:无分组时匹配所有字母和数字组合 ------------
- a = "abc123_abc456"
- b = re.findall(r"[a-z]+\d*", a)
- print(b) # 输出: ['abc123', 'abc456']
- # ------------ 示例2:分组时仅提取分组内容 ------------
- a = "abc123_abc456_789"
- b = re.findall(r"[a-z]+(\d*)", a) # 强调只提取括号内的 \d*
- print(b) # 输出: ['123', '456', ''](注意最后一个匹配的 \d* 为空)
- # ------------ 示例3:多分组时提取元组列表 ------------
- a = "abc123abc456哈哈哈哈哈哈abc789abc901"
- b = re.findall(r"[a-z]+(\d*)[a-z]+(\d*)", a)
- print(b) # 输出: [('123', '456'), ('789', '901')]
4. re.sub(pattern, repl, string, count=0)
- 功能:替换所有匹配项(或前 count 个)。
- 参数说明:
- pattern:正则表达式。
- repl:替换内容(字符串或函数)。
- string:目标字符串。
- count:最大替换次数(默认 0 表示全部替换)。
- 返回值:替换后的新字符串。
- 示例:
- match_obj = re.sub("\d+", '18', "周周已经30岁了,蓓蓓才10岁", count=0)
- if match_obj:
- # 获取匹配结果数据
- print(match_obj)
- else:
- print("匹配失败")
5. re.split(pattern, string, maxsplit=0)
- 功能:按正则表达式分割字符串。
- 参数说明:
- pattern:正则表达式。
- string:目标字符串。
- maxsplit:最大分割次数(默认 0 表示全部分割)。
- 返回值:分割后的字符串列表。
- 示例:
- # ------------ 示例1:使用正则表达式分割复杂分隔符 ------------
- text = '香蕉,苹果、菠萝,栗子。猕猴桃'
- pattern = r'[,,、。]' # 匹配逗号、中文逗号、顿号、句号
- result = re.split(pattern, text)
- print(result) # 输出: ['香蕉', '苹果', '菠萝', '栗子', '猕猴桃']
- # ------------ 示例2:限制分割次数 ------------
- text = '香蕉、苹果、菠萝,栗子。猕猴桃'
- result = re.split(r'[,,、。]', text, maxsplit=2)
- print(result) # 输出: ['香蕉', '苹果', '菠萝,栗子。猕猴桃']
高级技巧【拓展】
- flags参数:
正则表达式的 flags 参数用于控制匹配模式,所有方法均支持此参数(如 re.match(), re.search(), re.findall(), re.sub(), re.split() 等)。以下是常用修饰符:
修饰符 |
简写 |
作用 |
示例场景 |
re.IGNORECASE |
re.I |
忽略大小写 |
匹配 "Hello" 和 "hello" |
re.MULTILINE |
re.M |
多行模式(^ 和 $ 匹配每行) |
处理多行日志 |
re.DOTALL |
re.S |
. 匹配包括换行符的所有字符 |
提取跨行文本 |
re.ASCII |
- |
仅匹配 ASCII 字符 |
过滤非英文字符 |
使用示例:
- # 示例1:忽略大小写匹配(re.IGNORECASE)
- text = "Apple Banana"
- matches = re.findall(r"apple", text, flags=re.IGNORECASE)
- print(matches) # 输出: ['Apple']
- # 示例2:多行模式(re.MULTILINE)
- text = "Line1\nLine2"
- matches = re.findall(r"^Line\d", text, flags=re.MULTILINE)
- print(matches) # 输出: ['Line1', 'Line2']
- # 示例3:组合多个修饰符(re.I | re.S)
- text = "Start\nEnd"
- match = re.search(r"start.*end", text, flags=re.I | re.S)
- print(match.group()) # 输出: Start\nEnd
2. 预编译正则表达式
什么是预编译正则表达式?
- 定义:通过 re.compile() 将正则表达式字符串转换为 Pattern 对象,后续可直接调用该对象的方法(如 match(), search())。
- 核心作用:提升性能(避免重复解析正则表达式),并简化代码复用。
基本使用方法示例:
步骤:
-
- 使用 re.compile() 编译正则表达式,生成 Pattern 对象。
- 调用 pattern 对象的方法(如 match(), search()),参数与原 re 模块方法一致。
- # 示例:在循环中重复匹配
- import re
- # 1.预编译正则表达式
- pattern = re.compile(r"\d+")
- data_list = ["user123", "item456", "price789"] # 需要匹配的字符串
- for text in data_list:
- # 2.调用预编译对象pattern的方法
- match = pattern.search(text)
- print(match.group()) # 输出: 123, 456, 789
多方法复用示例:
就是同一正则表达式需用于 match、search、findall 等多种操作时。
- pattern = re.compile(r"\b\w+\b") # 预编译
- text = "hello world"
- # 复用同一个 Pattern 对象
- print(pattern.match(text).group()) # 输出: hello
- print(pattern.findall(text)) # 输出: ['hello', 'world']
- 贪婪和非贪婪匹配
1. 贪婪匹配
- 定义:默认模式,匹配时尽可能多地吞并字符,直到无法满足后续条件为止。
- 语法:使用 *, +, ?, {n,m} 等量词时,默认是贪婪的。
- 示例:
- import re
- s = "A phone number 130-9411-2846"
- r = re.match(r"(.+)(\d+-\d+-\d+)", s)
- print(r.group(1)) # 输出: A phone number 13
- print(r.group(2)) # 输出: 0-9411-2846(不符合预期!)
-
- 问题原因:.+ 贪婪吞并尽可能多字符,直到最后一个 \d 才停止。
2. 非贪婪匹配
- 定义:匹配时尽可能少地吞并字符,一旦满足条件立即结束。
- 语法:在量词后添加 ?,如 *?, +?, ??。
- 示例:
- s = "A phone number 130-9411-2846"
- r = re.match(r"(.+?)(\d+-\d+-\d+)", s)
- print(r.group(1)) # 输出: A phone number
- print(r.group(2)) # 输出: 130-9411-2846(正确分割!)
-
- 解决原因:.+? 非贪婪匹配到第一个 \d 前即停止。
3. 对比总结
类型 |
语法 |
行为 |
适用场景 |
贪婪匹配 |
.+ |
尽可能多匹配字符 |
需要吞并大部分内容时 |
非贪婪匹配 |
.+? |
尽可能少匹配字符 |
需要精准分割前后内容时 |
- 原生字符串 r 的作用
之前的正则中,需要用反斜杠 \ 对一些特殊字符进行转译,但是如果我们就是需要去匹配反斜杠呢?此时可以用到r写在正则的匹配规则之前,做到将正则的规则字符串看作原生的字符,即为不需要再去转译的字符。
- 反斜杠 \ 的转义冲突:
正则表达式中需用 \d、\w 等语法,而 Python 字符串本身用 \ 表示转义,导致需写多个 \。
- 示例:
- # 目标:匹配字符串 "d:\a\b\c"
- m = "d:\\a\\b\\c" # Python 字符串需转义为 "d:\\a\\b\\c"
- # 未用原生字符串写法
- ret = re.match("d:\\\\a", m) # 需写4个反斜杠!
- print(ret.group()) # 输出: d:\a(实际匹配到 "d:\\a")
- # 使用原生字符串写法
- ret = re.match(r"d:\\a", m) # 只需写2个反斜杠
- print(ret.group()) # 输出: d:\a(匹配到 "d:\\a")
2. 原生字符串 r 的作用
- 功能:取消 Python 字符串的转义,使 \ 直接表示字面意义。
- 语法:在字符串前加 r,如 r"\d+"。
- 对比示例:
- print("\\") # 输出: \
- print(r"\\") # 输出: \\
- # 示例:非贪婪匹配 + 原生字符串
- text = "Price: $100.99, Tax: $20.5"
- pattern = re.compile(r"\$(.+?)\b") # 匹配金额(非贪婪)
- matches = pattern.findall(text)
- print(matches) # 输出: ['100.99', '20.5']