正则表达式是处理文本数据的强大工具,Python通过re
模块提供了完整的正则表达式功能。本文将详细介绍Python正则表达式的使用方法,包括基础语法、高级技巧和re
模块API的详细解析。
一、正则表达式基础
1.1 什么是正则表达式
正则表达式(Regular Expression)是一种用于匹配字符串中字符组合的模式,可以用于搜索、替换和验证文本数据。
1.2 Python中的re模块
Python通过内置的re
模块提供正则表达式支持:
import re
二、正则表达式基本语法
2.1 普通字符
大多数字母和字符只会匹配它们自身:
pattern = r"hello"
text = "hello world"
match = re.search(pattern, text)
if match:
print("找到匹配:", match.group()) # 输出: 找到匹配: hello
2.2 元字符
正则表达式中具有特殊含义的字符:
.
匹配任意单个字符(除了换行符)^
匹配字符串的开头$
匹配字符串的结尾*
匹配前面的子表达式零次或多次+
匹配前面的子表达式一次或多次?
匹配前面的子表达式零次或一次{m,n}
匹配前面的子表达式m到n次[]
字符集,匹配其中任意一个字符|
或操作,匹配左边或右边的表达式()
分组,标记一个子表达式的开始和结束位置
2.3 字符类
\d
匹配任意数字,等价于[0-9]\D
匹配任意非数字字符\s
匹配任意空白字符(空格、制表符、换行符等)\S
匹配任意非空白字符\w
匹配任意字母数字字符,等价于[a-zA-Z0-9_]\W
匹配任意非字母数字字符
三、re模块API详解
3.1 re.compile(pattern, flags=0)
编译正则表达式模式,返回一个正则表达式对象。
参数说明:
pattern
: 要编译的正则表达式字符串flags
: 可选标志,用于修改正则表达式的匹配方式
常用flags:
re.IGNORECASE
或re.I
: 忽略大小写re.MULTILINE
或re.M
: 多行模式,影响^和$re.DOTALL
或re.S
: 使.匹配包括换行符在内的所有字符
示例:
# 编译一个正则表达式对象
pattern = re.compile(r'\d{3}-\d{3}-\d{4}', re.IGNORECASE)
# 使用编译后的对象进行匹配
text = "我的电话号码是123-456-7890"
match = pattern.search(text)
if match:
print("找到电话号码:", match.group()) # 输出: 找到电话号码: 123-456-7890
3.2 re.search(pattern, string, flags=0)
扫描整个字符串并返回第一个成功的匹配。
参数说明:
pattern
: 要匹配的正则表达式string
: 要搜索的字符串flags
: 可选标志
示例:
text = "Python是一种流行的编程语言,Python简单易学"
match = re.search(r'Python', text)
if match:
print("找到匹配:", match.group()) # 输出: 找到匹配: Python
print("匹配位置:", match.span()) # 输出: 匹配位置: (0, 6)
3.3 re.match(pattern, string, flags=0)
尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,就返回None。
与search的区别:
match
只在字符串开头匹配search
在整个字符串中搜索第一个匹配
示例:
text1 = "Python很棒"
text2 = "学习Python很棒"
print(re.match(r'Python', text1)) # 返回匹配对象
print(re.match(r'Python', text2)) # 返回None
3.4 re.findall(pattern, string, flags=0)
返回字符串中所有与模式匹配的非重叠匹配项,作为字符串列表。
示例:
text = "苹果10元,香蕉5元,橙子8元"
prices = re.findall(r'\d+元', text)
print(prices) # 输出: ['10元', '5元', '8元']
3.5 re.finditer(pattern, string, flags=0)
返回一个迭代器,产生所有非重叠匹配的匹配对象。
与findall的区别:
findall
返回字符串列表finditer
返回匹配对象迭代器
示例:
text = "Python 3.8, Python 3.9, Python 3.10"
matches = re.finditer(r'Python \d+\.\d+', text)
for match in matches:
print(f"找到: {match.group()} 在位置 {match.span()}")
# 输出:
# 找到: Python 3.8 在位置 (0, 9)
# 找到: Python 3.9 在位置 (11, 20)
# 找到: Python 3.10 在位置 (22, 32)
3.6 re.sub(pattern, repl, string, count=0, flags=0)
替换字符串中的匹配项。
参数说明:
pattern
: 正则表达式模式repl
: 替换的字符串或函数string
: 原始字符串count
: 最大替换次数,0表示替换所有flags
: 可选标志
示例:
text = "今天是2023-05-15,明天是2023-05-16"
# 替换日期格式
new_text = re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\1年\2月\3日', text)
print(new_text) # 输出: 今天是2023年05月15日,明天是2023年05月16日
# 使用函数作为替换
def to_upper(match):
return match.group().upper()
text = "hello world"
new_text = re.sub(r'\w+', to_upper, text)
print(new_text) # 输出: HELLO WORLD
3.7 re.split(pattern, string, maxsplit=0, flags=0)
按照能够匹配的子串将字符串分割后返回列表。
参数说明:
pattern
: 分隔符正则表达式string
: 要分割的字符串maxsplit
: 最大分割次数,0表示不限制flags
: 可选标志
示例:
text = "苹果,香蕉,,橙子, 西瓜"
# 按逗号分割,忽略空格和空字符串
items = re.split(r'\s*,\s*', text.strip())
print(items) # 输出: ['苹果', '香蕉', '', '橙子', '西瓜']
# 使用多个分隔符
text = "苹果 香蕉,橙子;西瓜"
items = re.split(r'[ ,;]', text)
print(items) # 输出: ['苹果', '香蕉', '橙子', '西瓜']
四、匹配对象的方法
当使用search()
或match()
成功匹配后,会返回一个匹配对象,该对象有以下方法:
4.1 group([group1, ...])
返回匹配的一个或多个子组。
示例:
text = "John Doe, 30岁"
match = re.search(r'(\w+) (\w+), (\d+)岁', text)
if match:
print("完整匹配:", match.group(0)) # 输出: 完整匹配: John Doe, 30岁
print("名字:", match.group(1)) # 输出: 名字: John
print("姓氏:", match.group(2)) # 输出: 姓氏: Doe
print("年龄:", match.group(3)) # 输出: 年龄: 30
print("所有组:", match.groups()) # 输出: 所有组: ('John', 'Doe', '30')
4.2 groups(default=None)
返回一个包含所有子组的元组。
4.3 groupdict(default=None)
返回一个包含所有命名子组的字典,键为子组名。
4.4 start([group]) 和 end([group])
返回匹配的子组的开始和结束位置。
4.5 span([group])
返回一个元组包含匹配的子组的 (开始, 结束) 位置。
五、高级正则表达式技巧
5.1 非贪婪匹配
默认情况下,*
和+
是贪婪的,会匹配尽可能多的字符。添加?
使其变为非贪婪:
text = "<h1>标题</h1><p>段落</p>"
# 贪婪匹配
greedy = re.search(r'<.*>', text)
print(greedy.group()) # 输出: <h1>标题</h1><p>段落</p>
# 非贪婪匹配
non_greedy = re.search(r'<.*?>', text)
print(non_greedy.group()) # 输出: <h1>
5.2 前向断言和后向断言
(?=...)
正向前视断言(?!...)
负向前视断言(?<=...)
正向后视断言(?<!...)
负向后视断言
示例:
# 匹配后面跟着"元"的数字
text = "苹果10元,香蕉5元,橙子8个"
prices = re.findall(r'\d+(?=元)', text)
print(prices) # 输出: ['10', '5']
# 匹配前面是"价格:"的数字
text = "价格:100,数量:5"
numbers = re.findall(r'(?<=价格:)\d+', text)
print(numbers) # 输出: ['100']
5.3 命名组
使用(?P<name>...)
语法为组命名:
text = "2023-05-15"
match = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', text)
if match:
print(match.groupdict()) # 输出: {'year': '2023', 'month': '05', 'day': '15'}
5.4 条件匹配
使用(?(id/name)yes-pattern|no-pattern)
:
# 如果第一个组匹配"Mr",则匹配"Smith",否则匹配"Smithson"
text1 = "Mr Smith"
text2 = "Mrs Smithson"
pattern = r'(Mr)? (?(1)Smith|Smithson)'
print(re.match(pattern, text1)) # 匹配
print(re.match(pattern, text2)) # 匹配
六、实际应用示例
6.1 验证电子邮件地址
def validate_email(email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
print(validate_email("test@example.com")) # True
print(validate_email("invalid.email@")) # False
6.2 提取URL信息
def extract_url_info(url):
pattern = r'(https?)://([^/]+)(/.*)?'
match = re.match(pattern, url)
if match:
return {
'protocol': match.group(1),
'domain': match.group(2),
'path': match.group(3) or '/'
}
return None
url_info = extract_url_info("https://www.example.com/path/to/page")
print(url_info)
# 输出: {'protocol': 'https', 'domain': 'www.example.com', 'path': '/path/to/page'}
6.3 日志分析
log_line = '127.0.0.1 - - [10/May/2023:15:32:45 +0800] "GET /index.html HTTP/1.1" 200 1234'
pattern = r'^(\S+) \S+ \S+ \[([^\]]+)\] "(\S+) (\S+) (\S+)" (\d+) (\d+)'
match = re.match(pattern, log_line)
if match:
log_data = {
'ip': match.group(1),
'time': match.group(2),
'method': match.group(3),
'path': match.group(4),
'protocol': match.group(5),
'status': int(match.group(6)),
'size': int(match.group(7))
}
print(log_data)
# 输出: {'ip': '127.0.0.1', 'time': '10/May/2023:15:32:45 +0800',
# 'method': 'GET', 'path': '/index.html', 'protocol': 'HTTP/1.1',
# 'status': 200, 'size': 1234}
七、性能优化建议
预编译正则表达式:对于重复使用的正则表达式,使用
re.compile()
预先编译。使用非贪婪匹配:当可能时,使用非贪婪限定符
*?
、+?
等。避免回溯灾难:复杂的正则表达式可能导致性能问题,尽量简化。
使用原子组:
(?>...)
可以防止回溯。合理使用字符类:
[abc]
比(a|b|c)
更高效。
八、常见问题与解决方案
8.1 匹配多行文本
使用re.MULTILINE
标志:
text = """第一行
第二行
第三行"""
matches = re.findall(r'^第\w+', text, re.MULTILINE)
print(matches) # 输出: ['第一行', '第二行', '第三行']
8.2 忽略大小写匹配
使用re.IGNORECASE
标志:
text = "Python python PYTHON"
matches = re.findall(r'python', text, re.IGNORECASE)
print(matches) # 输出: ['Python', 'python', 'PYTHON']
8.3 匹配Unicode字符
使用\u
或\x
转义,或直接包含Unicode字符:
text = "中文Chinese にほんご"
matches = re.findall(r'[\u4e00-\u9fa5]+', text) # 匹配中文字符
print(matches) # 输出: ['中文']
九、总结
Python的正则表达式功能强大而灵活,re
模块提供了丰富的API来处理各种文本匹配需求。掌握正则表达式可以大大提高文本处理的效率和能力。记住:
复杂的正则表达式可以先分解为多个简单的部分
使用
re.VERBOSE
标志可以使复杂的正则表达式更易读测试正则表达式时可以使用在线工具如regex101.com
对于非常复杂的文本处理,可能需要结合其他方法(如解析器)
希望本博客能帮助你掌握Python正则表达式的使用!