正则表达式
1.正则表达式简介
1.1 什么是正则表达式
正则表达式(Regular Expression,简称regex)是一种用于描述字符串匹配模式的强大工具。它是一种特殊的字符序列,可以帮助你方便地检查一个字符串是否与某种模式匹配,或者从字符串中提取满足某种模式的子串。
1.2 正则表达式的应用场景
- 数据验证 :如邮箱、手机号、身份证号等格式验证
emails = ["user@example.com", "invalid-email", "user.name@company.co.uk"]
- 数据提取 :从文本中提取特定格式的信息
text = "联系方式:13812345678,固话:010-12345678,客服:400-123-4567"
- 数据替换 :替换文本中的特定内容
text = "这个产品很垃圾,客服态度也很差劲!"
- 数据分割 :按照特定模式分割字符串
text = "姓名:张三;年龄:25;职业:工程师"
1.3 正则表达式的优势
- 灵活性 :可以描述各种复杂的字符串模式
- 简洁性 :用简短的表达式描述复杂的匹配规则
- 高效性 :相比于传统的字符串处理方法更加高效
2.正则表达式语法
2.1 基本字符匹配
| 字符 | 描述 |
|---|---|
| 普通字符 | 匹配自身,如 a 匹配字符"a" |
| . | 匹配除换行符外的任意一个字符 |
| \ | 转义字符,用于匹配特殊字符本身 |
案例:基本字符匹配
import re
# 原生字符串:所有字符都按照字符本身的描述进行打印 略过字符的特殊含义
print(r"ab\ncd")
print("ab\ncd")
text = "Hello World! World H1llo Hwllo H#llo H123llo"
# 普通字符匹配 search从头开始 第一次出现的匹配
# 含义:寻找 World 是否出现在 text中
match = re.search(r'World', text)
print(match.group())
# 点号匹配
arr = re.findall(r'H.llo', text)
print(arr)
arr = re.findall(r'H...llo', text)
print(arr)
text = "The coat is $100. $1204 $123213"
match = re.search(r'\$\d+', text)
print(match)
arr = re.findall(r'\$\d+', text)
print(arr)
text = "The coat is ¥100. ¥1204 ¥123213"
arr = re.findall(r'¥\d+', text)
print(arr)
2.2 字符类
| 表达式 | 描述 |
|---|---|
| [abc] | 匹配方括号内的任意字符 |
| [^abc] | 匹配除了方括号内字符的任意字符 |
| [a-z] | 匹配指定范围内的任意字符 |
| \d | 匹配任意数字,等价于 [0-9] |
| \D | 匹配任意非数字,等价于 [^0-9] |
| \w | 匹配任意字母、数字或下划线,等价于 [a-zA-Z0-9_] |
| \W | 匹配任意非字母、数字或下划线,等价于 [^a-zA-Z0-9_] |
| \s | 匹配任意空白字符(空格、制表符、换行符等)[ \t\n\r\f\v] |
| \S | 匹配任意非空白字符[^ \t\n\r\f\v] |
案例:字符类
import re
text = "The quick brown fox jumps over the lazy dog."
# 匹配所有的元音字母
arr = re.findall(r'[aeiou]', text.lower())
print(arr)
# 匹配所有的非元音字母
arr = re.findall(r'[^aeiou\s\.]', text.lower())
print(arr)
arr = re.findall(r'\d', "Phone: 123-456-789")
print(arr)
arr = re.findall(r'\w+', text)
print(arr)
2.3 边界匹配
| 表达式 | 描述 |
|---|---|
| ^ | 匹配字符串的开头 |
| $ | 匹配字符串的结尾 |
| \b | 匹配单词边界 |
| \B | 匹配非单词边界 |
案例:边界匹配
import re
print(re.search(r'^Python', "Python is cool"))
print(re.search(r'^Python', "I love python"))
print(re.search(r'Python$', "Python is cool"))
print(re.search(r'Python$', "I love Python"))
text = "Python programing is fun. I love Python!"
arr = re.findall(r'\bPython\b', text)
print(arr)
text = "word boundary example wordstart wordhaha xixiword endword awordlala abc123"
arr = re.findall(r'word\w+', text)
print(arr)
arr = re.findall(r'\bword\w+', text)
print(arr)
arr = re.findall(r'\w+word\b', text)
print(arr)
arr = re.findall(r'\b\w+\b', text)
print(arr)
arr = re.findall(r'\b[a-zA-Z]+\b', text)
print(arr)
2.4 数量限定符
| 量词 | 描述 | 示例 |
|---|---|---|
* | 匹配0次或多次 | ab* 匹配 “a”、“ab”、“abb” 等 |
+ | 匹配1次或多次 | ab+ 匹配 “ab”、“abb”,但不匹配 “a” |
? | 匹配0次或1次 | ab? 匹配 “a” 或 “ab” |
{n} | 精确匹配n次 | a{3} 匹配 “aaa” |
{n,} | 匹配至少n次 | a{2,} 匹配 “aa”、“aaa” 等 |
{n,m} | 匹配n到m次(包含n和m) | a{2,3} 匹配 “aa” 或 “aaa” |
案例:数量限定符
import re
text = "a ab abb abbb abbbb abbbbb abbbbbb abbbbbbb"
# a后面有>=0个b
arr = re.findall(r'ab*', text)
print(arr)
# a后面有>=1个b
arr = re.findall(r'ab+', text)
print(arr)
# a后面有0个或1个b
arr = re.findall(r'ab?', text)
print(arr)
# a后面有3个b
arr = re.findall(r'ab{3}', text)
print(arr)
# a后面有>=4个b
arr = re.findall(r'ab{4,}', text)
print(arr)
# a后面有2~4个b
arr = re.findall(r'ab{2,4}', text)
print(arr)
案例:验证邮箱格式
import re
def is_valid_email(email):
pattern = r'^[\w\.-]+@[\w\.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
emails = ["user@example.com", "invalid-email", "user.name@company.co.uk"]
for email in emails:
print(f'{email}:{is_valid_email(email)}')
3.Python中的re模块
3.1 re模块简介
Python的 re 模块提供了对正则表达式的支持,它包含了许多用于字符串处理的函数和方法。
3.2 re模块的主要函数
| 函数 | 描述 |
|---|---|
| re.match() | 从字符串的起始位置匹配,如果起始位置匹配成功,返回Match对象,否则返回None |
| re.search() | 扫描整个字符串,找到第一个匹配的位置,返回Match对象,否则返回None |
| re.findall() | 返回字符串中所有与模式匹配的子串列表 |
| re.finditer() | 返回一个迭代器,包含所有匹配的Match对象 |
| re.sub() | 替换字符串中与模式匹配的子串 |
| re.split() | 按照模式分割字符串 |
| re.complie() | 编译正则表达式模式,返回一个Pattern对象 |
3.3 Match对象的主要方法
| 方法 | 描述 |
|---|---|
| group() | 返回一个或多个分组匹配的字符串 |
| groups() | 返回一个包含所有分组的元组 |
| groupdict() | 返回一个包含所有命名分组的字典 |
| start() | 返回指定分组匹配的子串的起始位置 |
| end() | 返回指定分组匹配的子串的结束位置 |
| span() | 返回一个元组(start, end),表示指定分组匹配的子串的起始和结束位置 |
案例:使用re.match和re.search的区别
import re
text = "Python is awesome, Python is powerful"
# 简单理解 以XXX开头
match = re.match(r'Python', text)
print(match)
match = re.match(r'awesome', text)
print(match)
# 简单理解 检测XXX的存在性
match = re.search(r'powerful', text)
print(match)
match = re.search(r'java', text)
print(match)
match = re.search(r'Python', text)
print(match)
案例:使用re.findall和re.finditer
import re
text = "Python的版本有:Python2.7, Python3.6, Python3.8, Python3.9, Java8.8"
arr = re.findall(r'Python\d\.\d', text)
print(arr)
arr = re.findall(r'\d\.\d', text)
print(arr)
for match in re.finditer(r'Python(\d(\.\d))', text):
print(f'在位置{match.start()}:{match.end()}找到:{match.group(0)},版本号:{match.group(1)},=={match.group(2)}')
text = "联系方式:电话15388886868,邮箱zh@oupeng.com,微信wx_123456"
# | 逻辑或 匹配任意一个模式
arr = re.findall(r'1[3-9]\d{9}|[a-zA-Z0-9\._%+-]+@[\w\.-]+\.[a-zA-Z]{2,}|wx_[a-zA-Z\d]+', text)
print(arr)
for match in re.finditer(r'1[3-9]\d{9}|[a-zA-Z0-9\._%+-]+@[\w\.-]+\.[a-zA-Z]{2,}|wx_[a-zA-Z\d]+', text):
print(match.group())
print(match.span())
案例:使用re.sub进行替换
import re
text = "这个产品很垃圾,客服态度也很差劲!这他妈的是个傻逼,草泥马哦"
sensitive_words = ["垃圾", "差劲","傻逼","草泥马","约吗","CP","他妈的"]
for word in sensitive_words:
text = re.sub(word, "*" * len(word), text)
print(text)
# MM/DD/YYYY -> YYYY-MM-DD
# 用于拼接返回替代的字符串
def date_converter(match):
# (06)/(04)/(2025)
# 解析匹配字符串中的内容
month, day, year = match.groups()
return f'{year}-{month}-{day}'
text = "今天是06/04/2025, 明天是06/05/2025"
result = re.sub(r'(\d{2})/(\d{2})/(\d{4})', date_converter ,text)
print(result)
def date_converter_other(match):
month, day, year = tuple(match.group().split("/"))
return year + "-" + month + "-" + day
text = "今天是06/04/2025, 明天是06/05/2025"
result = re.sub(r'\d{2}/\d{2}/\d{4}', date_converter_other ,text)
print(result)
案例:使用re.split分割字符串
import re
text = "apple,banana;cherry|grape:orange"
# 切割返回的是一个列表
result = re.split(r'[,;|:]', text)
print(result)
# 限制分割次数
result = re.split(r'[,;|:]', text, maxsplit=2)
print(result)
text = "姓名:张三;年龄:25;职业:工程师"
parts = re.split(r'[;:]', text)
print(parts)
# 把切割出来的结果 进行字典化
keys = parts[::2] # 偶数位(角标)
print(keys)
values = parts[1::2] # 奇数位(角标)
print(values)
# 通过zip将两个列表进行打包 -> 一堆元组(键,值)
# 通过dict()函数 将一堆元组 进行字典化处理
person_dict = dict(zip(keys, values))
print(person_dict)
案例:使用re.compile预编译正则表达式
import re
import time
# 原本的正则表达式
pattern_str = r'^[\w\.-]+@[\w\.-]+\.[a-zA-Z]{2,}$'
# 被编译后的正则表达式-Pattern对象
email_pattern = re.compile(pattern_str)
# 创建一个大量的邮箱数据
emails = [f'user{i}@example.com' for i in range(10000)]
# 使用预编译模式
start_time = time.time()
valid_emails = [email for email in emails if email_pattern.match(email)]
end_time = time.time()
print(f'预编译方式耗时:{end_time - start_time:.6f}秒')
# 不使用预编译
start_time = time.time()
valid_emails = [email for email in emails if re.match(pattern_str, email)]
end_time = time.time()
print(f'非预编译方式耗时:{end_time - start_time:.6f}秒')
"""
Pattern.math(text) -> 匹配结果
re.match(pattern,text) -> Pattern.math(text) -> 匹配结果
"""
4.正则表达式分组和捕获
4.1 基本分组
使用圆括号()可以将正则表达式的一部分括起来形成一个分组。分组的作用:
- 将多个字符视为一个整体,应用量词
- 提取匹配结果中的特定部分
- 允许在正则表达式内部或外部引用分组内容
案例:基本分组
import re
# 提取年月日
date_str = "今天是2025-06-05,明天是2025-06-06"
pattern = r'(\d{4})-(\d{2})-(\d{2})'
for match in re.finditer(pattern, date_str):
print("完整的日期:", match.group())
print("完整的日期:", match.group(0)) # 完整的匹配
print("年份:", match.group(1))
print("月份:", match.group(2))
print("日期:", match.group(3))
print("所有的分组:", match.groups())
案例:从文本中提取所有电话号码
import re
text = "联系方式:15388886666,固话:010-12345678,客服:400-123-4567"
pattern = r'(1[3-9]\d{9}|\d{3,4}-\d{7,8}|\d{3,4}-\d{3,4}-\d{4})'
phone_numbers = re.findall(pattern, text)
print(phone_numbers)
for match in re.finditer(pattern, text):
print(match.group()) # 完全匹配
print(match.group(0))# 完全匹配
print(match.group(1))# 取分组中的内容 跟上述是一致的
4.2 命名分组
以通过(?P<name>...)语法为分组命名
案例:命名分组
import re
log_entry = "[2025-06-05 19:59:56] ERROR: Datebase connection failed"
pattern = r'\[((?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2}) (?P<time>\d{2}:\d{2}:\d{2}))\] (?P<level>\w+): (?P<message>.+)'
match = re.search(pattern, log_entry)
# 确保匹配成功
if match:
print(match.group()) # 完全匹配
print(match.group(0)) # 完全匹配
print(match.group(1))
print(match.group(2))
print(match.group(6))
# 就是为了避免角标操作
print(match.group('year'))
print(match.group('month'))
print(match.group('day'))
print(match.group('time'))
print(match.group('level'))
print(match.group('message'))
4.3 反向引用
在正则表达式内部,可以使用\数字或\g<name>引用前面定义的分组
案例:反向引用
import re
# 匹配重复单词
text = "hello hello world world"
pattern = r'\b(\w+)\s+\1\b'
# findall 如果正则表达式中没有分组 则默认全匹配 有分组 则寻找分组内容
result = re.findall(pattern, text)
print(result)
# 提取HTML标签内容
html = "<h1>标题</h1><p>段落内容1</p><p>段落内容2</p>"
pattern = r'<([a-z0-9]+)>(.*?)</\1>'
result = re.findall(pattern, html)
print(result)
# 使用命名反向引用
text = "2025-06-05" # -> 06/05/2025
pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
text = re.sub(pattern, r'\g<month>/\g<day>/\g<year>' , text)
print(text)
5.正则表达式修饰符
5.1 常用修饰符
| 修饰符 | 描述 |
|---|---|
re.I 或 re.IGNORECASE | 忽略大小写 |
re.M 或 re.MULTILINE | 多行模式,^和$匹配每行的开头和结尾 |
re.S 或 re.DOTALL | 点号.匹配所有字符,包括换行符 |
re.X 或 re.VERBOSE | 允许使用注释和空格,使正则表达式更易读 |
5.2 内联修饰符
可以在正则表达式内部使用 (?imsxau) 语法应用修饰符。
案例:忽略大小写
import re
text = "Python is awesome. PYTHON is powerful. python is easy to learn."
# 不忽略大小写
print(re.findall(r'python', text))
# 忽略大小写
print(re.findall(r'python', text, re.IGNORECASE))
# 内联修饰符
print(re.findall(r'(?i)python', text))
案例:多行模式
import re
text = """第一行开始
第二行开始
第三行开始
结束
"""
print(re.findall(r'^第.+行', text))
print(re.findall(r'^第.+行', text, re.MULTILINE))
print(re.findall(r'(?m)^第.+行', text))
案例:点号匹配所有字符
import re
text = """这是第一行
这是第二行
这是第三行
"""
match = re.search(r'第一行.*第三行', text)
print(match.group() if match else None)
match = re.search(r'第一行.*第三行', text, re.DOTALL)
print(match.group() if match else None)
match = re.search(r'(?s)第一行.*第三行', text)
print(match.group() if match else None)
6.贪婪与非贪婪匹配
6.1 贪婪匹配
默认情况下,量词( * , + , ? , {n,m} )是贪婪的,会尽可能多地匹配字符。
6.2 非贪婪匹配
在量词后面加上 ? (如 *? , +? , ?? , {n,m}? )使其变成非贪婪模式,会尽可能少地匹配字符。
案例:贪婪与非贪婪对比
import re
# {DIV_A{DIV_AA}}{DIV_B}
text = "<div>DIV_A<div>DIV_AA</div></div><div>DIV_B</div>"
pattern = r'<div>(.*)</div>'
match = re.search(pattern, text)
print("贪婪的结果:", match.group())
print("贪婪的结果", re.findall(pattern, text))
pattern = r'<div>(.*?)</div>'
match = re.search(pattern, text)
print("非贪婪的结果:", match.group())
print("非贪婪的结果:", re.findall(pattern, text))
# 一般建议对于XML/HTML格式的数据进行XPath解析
拓展:异常数据处理、递归匹配处理、性能优化
7万+

被折叠的 条评论
为什么被折叠?



