正则匹配
正则匹配
1. 导入 re 模块
1
import re
2. 基本方法
2.1 re.match() - 从字符串开头匹配
1
2
3
4
5
6
# 匹配字符串开头
result = re.match(r'hello', 'hello world')
if result:
print("Match found:", result.group()) # hello
else:
print("No match")
2.2 re.search() - 搜索整个字符串
1
2
3
4
5
6
# 搜索第一个匹配
result = re.search(r'world', 'hello world')
if result:
print("Found:", result.group()) # world
print("Start position:", result.start()) # 6
print("End position:", result.end()) # 11
2.3 re.findall() - 查找所有匹配
1
2
3
4
5
6
7
8
# 查找所有匹配的字符串
text = "苹果 10元,香蕉 5元,橙子 8元"
prices = re.findall(r'\d+元', text)
print(prices) # ['10元', '5元', '8元']
# 只提取数字
numbers = re.findall(r'\d+', text)
print(numbers) # ['10', '5', '8']
2.4 re.finditer() - 返回迭代器
1
2
3
4
5
6
# 返回匹配对象的迭代器
text = "今天是2023-10-01,明天是2023-10-02"
pattern = r'\d{4}-\d{2}-\d{2}'
for match in re.finditer(pattern, text):
print(f"Found: {match.group()} at {match.start()}-{match.end()}")
2.5 re.sub() - 替换字符串
1
2
3
4
5
6
7
8
9
10
11
12
# 替换匹配的内容
text = "今天是2023-10-01"
new_text = re.sub(r'\d{4}-\d{2}-\d{2}', 'XXXX-XX-XX', text)
print(new_text) # 今天是XXXX-XX-XX
# 使用函数进行替换
def replace_date(match):
return f"日期: {match.group()}"
text = "今天是2023-10-01"
new_text = re.sub(r'\d{4}-\d{2}-\d{2}', replace_date, text)
print(new_text) # 今天是日期: 2023-10-01
2.6 re.split() - 分割字符串
1
2
3
4
# 按正则表达式分割
text = "苹果,香蕉;橙子 西瓜"
items = re.split(r'[,;\s]+', text)
print(items) # ['苹果', '香蕉', '橙子', '西瓜']
3. 常用正则表达式模式
3.1 基础模式
1
2
3
4
5
6
7
8
9
10
11
12
# 匹配数字
re.findall(r'\d+', "我有100元") # ['100']
# 匹配单词
re.findall(r'\w+', "hello world!") # ['hello', 'world']
# 匹配空白字符
re.split(r'\s+', "a b c") # ['a', 'b', 'c']
# 匹配任意字符(除换行外)
re.search(r'a.b', "a b") # 匹配
re.search(r'a.b', "a\nb") # 不匹配
3.2 量词
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# * 0次或多次
re.findall(r'ab*', "a ab abb abbb") # ['a', 'ab', 'abb', 'abbb']
# + 1次或多次
re.findall(r'ab+', "a ab abb abbb") # ['ab', 'abb', 'abbb']
# ? 0次或1次
re.findall(r'ab?', "a ab abb") # ['a', 'ab', 'ab']
# {n} 恰好n次
re.findall(r'\d{3}', "123 4567 89") # ['123', '456']
# {n,} 至少n次
re.findall(r'\d{3,}', "12 123 1234") # ['123', '1234']
# {n,m} n到m次
re.findall(r'\d{2,4}', "1 12 123 1234 12345") # ['12', '123', '1234', '1234']
3.3 字符集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# [abc] 匹配a、b或c
re.findall(r'[abc]', "apple banana cherry") # ['a', 'b', 'a', 'a', 'c']
# [a-z] 匹配小写字母
re.findall(r'[a-z]+', "Hello World") # ['ello', 'orld']
# [^abc] 不匹配a、b、c
re.findall(r'[^aeiou]', "hello") # ['h', 'l', 'l']
# 常用字符集简写
# \d = [0-9]
# \D = [^0-9]
# \w = [a-zA-Z0-9_]
# \W = [^a-zA-Z0-9_]
# \s = 空白字符
# \S = 非空白字符
4. 分组和捕获
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 简单分组
text = "2023-10-01"
match = re.match(r'(\d{4})-(\d{2})-(\d{2})', text)
if match:
print(match.group()) # 2023-10-01
print(match.group(1)) # 2023
print(match.group(2)) # 10
print(match.group(3)) # 01
print(match.groups()) # ('2023', '10', '01')
# 命名分组
text = "John: 30"
match = re.match(r'(?P<name>\w+): (?P<age>\d+)', text)
if match:
print(match.group('name')) # John
print(match.group('age')) # 30
# 非捕获分组 (?:...)
text = "hello world"
match = re.match(r'(?:hello) (world)', text)
print(match.groups()) # ('world',) 只捕获world
5. 编译正则表达式(提高效率)
1
2
3
4
5
6
7
8
9
10
# 预编译正则表达式(适合多次使用)
pattern = re.compile(r'\d{3}-\d{4}')
# 使用编译后的模式
result1 = pattern.search("电话: 123-4567")
result2 = pattern.findall("电话1: 123-4567, 电话2: 890-1234")
# 编译时可以添加标志
pattern = re.compile(r'hello', re.IGNORECASE) # 忽略大小写
pattern.search("HELLO world") # 匹配
6. 标志(Flags)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 常用标志
text = "HELLO\nworld"
# re.I / re.IGNORECASE - 忽略大小写
re.findall(r'hello', text, re.I) # ['HELLO']
# re.M / re.MULTILINE - 多行模式
re.findall(r'^world', text, re.M) # ['world']
# re.S / re.DOTALL - 让.匹配包括换行符在内的所有字符
re.findall(r'HELLO.*world', text, re.S) # ['HELLO\nworld']
# re.X / re.VERBOSE - 允许添加注释和空白
pattern = re.compile(r'''
\d{3} # 区号
- # 分隔符
\d{8} # 电话号码
''', re.VERBOSE)
7. 实用示例
示例1:验证邮箱格式
1
2
3
4
5
6
def validate_email(email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
print(validate_email("test@example.com")) # True
print(validate_email("invalid-email")) # False
示例2:提取HTML标签内容
1
2
3
html = "<h1>标题</h1><p>段落内容</p>"
titles = re.findall(r'<h1>(.*?)</h1>', html)
paragraphs = re.findall(r'<p>(.*?)</p>', html)
示例3:提取URL参数
1
2
3
url = "https://example.com/page?name=John&age=30&city=Beijing"
params = re.findall(r'([^&=]+)=([^&=]+)', url)
print(dict(params)) # {'name': 'John', 'age': '30', 'city': 'Beijing'}
示例4:处理复杂文本
1
2
3
4
5
6
7
8
9
10
11
text = """
用户1: john@email.com, 电话: 138-1234-5678
用户2: jane@email.com, 电话: 139-8765-4321
"""
# 同时提取邮箱和电话
pattern = r'([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}).*?(\d{3}-\d{4}-\d{4})'
matches = re.findall(pattern, text, re.S)
for email, phone in matches:
print(f"邮箱: {email}, 电话: {phone}")
8. 注意事项
- 使用原始字符串(raw string):在正则表达式前加
r,避免转义问题 - 贪婪 vs 非贪婪:
.*是贪婪匹配(匹配尽可能多).*?是非贪婪匹配(匹配尽可能少)
- 性能考虑:多次使用的正则表达式应该先编译
- 边界匹配:使用
^和$匹配字符串开头和结尾 - 特殊字符需要转义:
. * + ? ^ $ { } [ ] ( ) | \
9. 调试工具
如果正则表达式复杂,可以使用在线工具调试:
本文由作者按照
CC BY 4.0
进行授权
