Python: 正则表达式

发布于:2025-07-07 ⋅ 阅读:(20) ⋅ 点赞:(0)

正则表达式是处理文本数据的强大工具,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}

七、性能优化建议

  1. 预编译正则表达式:对于重复使用的正则表达式,使用re.compile()预先编译。

  2. 使用非贪婪匹配:当可能时,使用非贪婪限定符*?+?等。

  3. 避免回溯灾难:复杂的正则表达式可能导致性能问题,尽量简化。

  4. 使用原子组(?>...)可以防止回溯。

  5. 合理使用字符类[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来处理各种文本匹配需求。掌握正则表达式可以大大提高文本处理的效率和能力。记住:

  1. 复杂的正则表达式可以先分解为多个简单的部分

  2. 使用re.VERBOSE标志可以使复杂的正则表达式更易读

  3. 测试正则表达式时可以使用在线工具如regex101.com

  4. 对于非常复杂的文本处理,可能需要结合其他方法(如解析器)

希望本博客能帮助你掌握Python正则表达式的使用!