ToolkitX
知识库工具箱

正则表达式深入

正则语法、贪婪/非贪婪、分组

25min·进阶

01. 贪婪与非贪婪匹配

正则表达式的贪婪和非贪婪匹配决定了量词的行为方式。 贪婪匹配(默认): - 尽可能多地匹配字符 - 从左到右尝试,直到失败 非贪婪匹配(懒惰匹配): - 在量词后加 ? 使匹配尽可能少 - 如 *?、+?、??、{n,m}?
python
import re

text = '<div>内容1</div><div>内容2</div>'

# 贪婪匹配(默认)
greedy = re.findall(r'<div>.*</div>', text)
print("贪婪:", greedy)
# ['<div>内容1</div><div>内容2</div>']

# 非贪婪匹配
lazy = re.findall(r'<div>.*?</div>', text)
print("非贪婪:", lazy)
# ['<div>内容1</div>', '<div>内容2</div>']

# 实际应用:提取 HTML 内容
html = '<p>第一段</p><p>第二段</p><p>第三段</p>'
paragraphs = re.findall(r'<p>(.*?)</p>', html)
print("段落:", paragraphs)
# ['第一段', '第二段', '第三段']

# 提取引号中的内容
text2 = '他说"你好",然后说"再见"'
quotes = re.findall(r'"(.*?)"', text2)
print("引号内容:", quotes)
# ['你好', '再见']

02. 分组与捕获

分组是正则表达式的核心特性,用于捕获匹配的子串。 分组类型: - 普通分组:(pattern) - 命名分组:(?P<name>pattern) - 非捕获分组:(?:pattern) - 前向断言:(?=pattern)、(?<=pattern) - 后向断言:(?!pattern)、(?<!pattern)
python
import re

# 普通分组
text = "2026-01-15"
match = re.search(r'(\d{4})-(\d{2})-(\d{2})', text)
if match:
print("完整匹配:", match.group(0))  # 2026-01-15
print("年份:", match.group(1))      # 2026
print("月份:", match.group(2))      # 01
print("日期:", match.group(3))      # 15

# 命名分组
match = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', text)
if match:
print("年份:", match.group('year'))
print("月份:", match.group('month'))

# 非捕获分组
text = "http://example.com https://secure.com"
urls = re.findall(r'(?:http|https)://([^/]+)', text)
print("域名:", urls)

# 前向断言(肯定)
text = "价格: 100元 数量: 200"
numbers = re.findall(r'\d+(?=元)', text)  # 后面是"元"的数字
print("价格:", numbers)  # ['100']

# 后向断言(肯定)
numbers2 = re.findall(r'(?<=数量: )\d+', text)  # 前面是"数量: "的数字
print("数量:", numbers2)  # ['200']

# 分组替换
text = "张三 13812345678 李四 13987654321"
masked = re.sub(r'(\d{3})\d{4}(\d{4})', r'\1\2', text)
print("脱敏:", masked)

03. 断言与零宽断言

断言(零宽断言)匹配一个位置而不是字符,用于精确匹配。 四种零宽断言: - (?=pattern):肯定先行断言(后面是 pattern) - (?!pattern):否定先行断言(后面不是 pattern) - (?<=pattern):肯定后行断言(前面是 pattern) - (?<!pattern):否定后行断言(前面不是 pattern)
python
import re

# 肯定先行断言 (?=...)
# 匹配后面跟着 "元" 的数字
text = "价格: 100元 数量: 200个"
result = re.findall(r'\d+(?=元)', text)
print("价格:", result)  # ['100']

# 否定先行断言 (?!...)
# 匹配后面不跟着 "个" 的数字
result2 = re.findall(r'\d+(?!个)', text)
print("不含个的数字:", result2)

# 肯定后行断言 (?<=...)
# 匹配前面是 "价格: " 的数字
result3 = re.findall(r'(?<=价格: )\d+', text)
print("价格:", result3)  # ['100']

# 否定后行断言 (?<!...)
# 匹配前面不是 "价格: " 的数字
result4 = re.findall(r'(?<!价格: )\d+', text)
print("其他数字:", result4)

# 实际应用:提取 HTML 标签内容(不使用分组)
html = '<b>粗体</b> 和 <i>斜体</i>'
tags = re.findall(r'(?<=<b>).*?(?=</b>)', html)
print("粗体内容:", tags)

# 密码强度验证
def check_password(password):
patterns = {
    '长度': r'(?=^.{8,})',
    '大写字母': r'(?=.*[A-Z])',
    '小写字母': r'(?=.*[a-z])',
    '数字': r'(?=.*\d)',
    '特殊字符': r'(?=.*[@$!%*?&])'
}
for name, pattern in patterns.items():
    if not re.search(pattern, password):
        return f"缺少: {name}"
return "密码强度合格"

print(check_password("Abc123@#"))

知识测验

1/4正确 0

贪婪匹配和非贪婪匹配的区别是什么?

下一节

操作系统基础

下一节