Python3 正则表达式详解

        正则表达式是用于处理字符串的强大工具,Python 通过 re 模块提供完整的正则表达式功能。

正则表达式修饰符 - 可选标志

        正则表达式可以包含一些可选标志修饰符来控制匹配的模式。

        以下标志可以单独使用,也可以通过按位或(|)组合使用。例如,re.IGNORECASE | re.MULTILINE 表示同时启用忽略大小写和多行模式。

修饰符描述实例
re.IGNORECASE 或 re.I使匹配对大小写不敏感
import re
pattern = re.compile(r'apple', flags=re.IGNORECASE)
result = pattern.match('Apple')
print(result.group())  # 输出: 'Apple'
re.MULTILINE 或 re.M多行匹配,影响 ^ 和 $,使它们匹配字符串的每一行的开头和结尾。
import re
pattern = re.compile(r'^\d+', flags=re.MULTILINE)
text = '123\n456\n789'
result = pattern.findall(text)
print(result)  # 输出: ['123', '456', '789']
re.DOTALL 或 re.S:使 . 匹配包括换行符在内的任意字符。
import re
pattern = re.compile(r'a.b', flags=re.DOTALL)
result = pattern.match('a\nb')
print(result.group())  # 输出: 'a\nb'
re.ASCII使 \w, \W, \b, \B, \d, \D, \s, \S 仅匹配 ASCII 字符。
import re
pattern = re.compile(r'\w+', flags=re.ASCII)
result = pattern.match('Hello123')
print(result.group())  # 输出: 'Hello123'
re.VERBOSE 或 re.X忽略空格和注释,可以更清晰地组织复杂的正则表达式。
import re
pattern = re.compile(r'''
    \d+  # 匹配数字
    [a-z]+  # 匹配小写字母
''', flags=re.VERBOSE)
result = pattern.match('123abc')
print(result.group())  # 输出: '123abc'

正则表达式模式

        模式字符串使用特殊的语法来表示一个正则表达式。

                字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。

多数字母和数字前加一个反斜杠时会拥有不同的含义。

        标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。

        反斜杠本身需要使用反斜杠转义。

        由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r'\t',等价于 \\t )匹配相应的特殊字符。

        下表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。

模式描述
^匹配字符串的开头
$匹配字符串的末尾。
.匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[...]用来匹配所包含的任意一个字符,例如 [amk] 匹配 'a','m'或'k'
[^...]不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
re*匹配0个或多个的表达式。
re+匹配1个或多个的表达式。
re?匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
re{ n}匹配n个前面表达式。例如,"o{2}"不能匹配"Bob"中的"o",但是能匹配"food"中的两个o。
re{ n,}精确匹配n个前面表达式。例如,"o{2,}"不能匹配"Bob"中的"o",但能匹配"foooood"中的所有o。"o{1,}"等价于"o+"。"o{0,}"则等价于"o*"。
re{ n, m}匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
a| b匹配a或b
(re)匹配括号内的表达式,也表示一个组
(?imx)正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。
(?-imx)正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
(?: re)类似 (...), 但是不表示一个组
(?imx: re)在括号中使用i, m, 或 x 可选标志
(?-imx: re)在括号中不使用i, m, 或 x 可选标志
(?#...)注释.
(?= re)前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。
(?! re)前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功。
(?> re)匹配的独立模式,省去回溯。
\w匹配数字字母下划线
\W匹配非数字字母下划线
\s匹配任意空白字符,等价于 [\t\n\r\f]。
\S匹配任意非空字符
\d匹配任意数字,等价于 [0-9]。
\D匹配任意非数字
\A匹配字符串开始
\Z匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。
\z匹配字符串结束
\G匹配最后匹配完成的位置。
\b匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\n, \t, 等。匹配一个换行符。匹配一个制表符, 等
\1...\9匹配第n个分组的内容。
\10匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。

1. 基本语法和特殊字符

常用元字符

import re

# . - 匹配任意单个字符(除了换行符)
text = "cat bat hat rat"
result = re.findall(r".at", text)
print(result)  # ['cat', 'bat', 'hat', 'rat']

# ^ - 匹配字符串开头
text = "hello world"
result = re.findall(r"^hello", text)
print(result)  # ['hello']

# $ - 匹配字符串结尾
text = "hello world"
result = re.findall(r"world$", text)
print(result)  # ['world']

# * - 匹配前一个字符0次或多次
text = "ct cat caat caaat"
result = re.findall(r"ca*t", text)
print(result)  # ['ct', 'cat', 'caat', 'caaat']

# + - 匹配前一个字符1次或多次
result = re.findall(r"ca+t", text)
print(result)  # ['cat', 'caat', 'caaat']

# ? - 匹配前一个字符0次或1次
result = re.findall(r"ca?t", text)
print(result)  # ['ct', 'cat']

字符集和范围

# [] - 字符集,匹配括号内的任意字符
text = "cat bat hat rat"
result = re.findall(r"[bc]at", text)
print(result)  # ['cat', 'bat']

# [^] - 否定字符集
result = re.findall(r"[^bc]at", text)
print(result)  # ['hat', 'rat']

# [a-z] - 字符范围
text = "a1 b2 c3 d4 A5 B6"
result = re.findall(r"[a-z]\d", text)
print(result)  # ['a1', 'b2', 'c3', 'd4']

# | - 或操作
text = "cat dog bird"
result = re.findall(r"cat|dog", text)
print(result)  # ['cat', 'dog']

2. 预定义字符集

# \d - 数字字符,等价于 [0-9]
text = "电话:123-4567-890"
result = re.findall(r"\d+", text)
print(result)  # ['123', '4567', '890']

# \D - 非数字字符
result = re.findall(r"\D+", text)
print(result)  # ['电话:', '-', '-']

# \w - 单词字符,等价于 [a-zA-Z0-9_]
text = "user_name123 @email.com"
result = re.findall(r"\w+", text)
print(result)  # ['user_name123', 'email', 'com']

# \W - 非单词字符
result = re.findall(r"\W+", text)
print(result)  # [' ', ' @', '.']

# \s - 空白字符(空格、制表符、换行符等)
text = "hello\tworld\npython"
result = re.findall(r"\s+", text)
print(result)  # ['\t', '\n']

# \S - 非空白字符
result = re.findall(r"\S+", text)
print(result)  # ['hello', 'world', 'python']

3. 量词和分组

量词

# {n} - 精确匹配n次
text = "color colour colouur"
result = re.findall(r"colou?r", text)  # ? 匹配0次或1次
print(result)  # ['color', 'colour']

# {n,} - 匹配至少n次
# {n,m} - 匹配n到m次
text = "a aa aaa aaaa aaaaa"
result = re.findall(r"a{2,4}", text)
print(result)  # ['aa', 'aaa', 'aaaa', 'aaaa'] 注意:'aaaaa'被匹配为'aaaa'

# 贪婪匹配 vs 非贪婪匹配
text = "<div>content</div><div>more</div>"
# 贪婪匹配
greedy = re.findall(r"<div>.*</div>", text)
print("贪婪:", greedy)  # ['<div>content</div><div>more</div>']

# 非贪婪匹配
non_greedy = re.findall(r"<div>.*?</div>", text)
print("非贪婪:", non_greedy)  # ['<div>content</div>', '<div>more</div>']

分组

# () - 捕获分组
text = "2023-10-25 2024-01-15"
result = re.findall(r"(\d{4})-(\d{2})-(\d{2})", text)
print(result)  # [('2023', '10', '25'), ('2024', '01', '15')]

# 命名分组 (?P<name>...)
text = "姓名: 张三, 年龄: 25"
pattern = r"姓名: (?P<name>\w+), 年龄: (?P<age>\d+)"
match = re.search(pattern, text)
if match:
    print(f"姓名: {match.group('name')}")  # 姓名: 张三
    print(f"年龄: {match.group('age')}")   # 年龄: 25

# 非捕获分组 (?:...)
text = "hello world hello python"
result = re.findall(r"(?:hello) (world|python)", text)
print(result)  # ['world', 'python']

4. re模块主要函数

re.match() - 从字符串开头匹配

def test_match():
    text = "hello world"
    
    # 匹配开头
    result = re.match(r"hello", text)
    if result:
        print("match found:", result.group())  # hello
    
    # 不匹配开头
    result = re.match(r"world", text)
    if not result:
        print("no match for 'world' at beginning")

test_match()

re.search() - 搜索整个字符串

def test_search():
    text = "hello world"
    
    # 搜索任意位置
    result = re.search(r"world", text)
    if result:
        print("search found:", result.group())  # world
        print("position:", result.span())      # (6, 11)

test_search()

re.findall() - 查找所有匹配

def test_findall():
    text = "苹果 10元, 香蕉 5元, 橙子 8元"
    
    # 查找所有价格
    prices = re.findall(r"\d+元", text)
    print("所有价格:", prices)  # ['10元', '5元', '8元']
    
    # 查找所有商品和价格
    items = re.findall(r"(\w+) (\d+)元", text)
    print("商品和价格:", items)  # [('苹果', '10'), ('香蕉', '5'), ('橙子', '8')]

test_findall()

re.finditer() - 返回迭代器

def test_finditer():
    text = "苹果 10元, 香蕉 5元, 橙子 8元"
    
    for match in re.finditer(r"(\w+) (\d+)元", text):
        print(f"商品: {match.group(1)}, 价格: {match.group(2)}元")
        print(f"位置: {match.span()}")

test_finditer()

re.sub() - 替换匹配内容

def test_sub():
    text = "今天是2023-10-25,明天是2023-10-26"
    
    # 替换日期格式
    result = re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\1年\2月\3日", text)
    print(result)  # 今天是2023年10月25日,明天是2023年10月26日
    
    # 使用函数进行替换
    def double_number(match):
        num = int(match.group())
        return str(num * 2)
    
    text2 = "数字: 1, 2, 3, 4, 5"
    result2 = re.sub(r"\d+", double_number, text2)
    print(result2)  # 数字: 2, 4, 6, 8, 10

test_sub()

re.split() - 根据模式分割字符串

def test_split():
    text = "苹果,香蕉;橙子 葡萄|梨"
    
    # 使用多种分隔符
    result = re.split(r"[,; \|]+", text)
    print("分割结果:", result)  # ['苹果', '香蕉', '橙子', '葡萄', '梨']
    
    # 保留分隔符
    result2 = re.split(r"([,;])", text)
    print("保留分隔符:", result2)

test_split()

5. 编译正则表达式

对于需要重复使用的模式,可以编译以提高效率:

def compiled_regex():
    # 编译正则表达式
    pattern = re.compile(r"\b\w{4}\b")  # 匹配4字母单词
    
    text = "This is a test string with some words"
    
    # 使用编译后的模式
    result1 = pattern.findall(text)
    print("4字母单词:", result1)  # ['This', 'test', 'with', 'some']
    
    result2 = pattern.sub("****", text)
    print("替换后:", result2)  # **** is a **** string with **** words

compiled_regex()

6. 标志参数

def regex_flags():
    text = "Hello\nWorld\nPython"
    
    # re.IGNORECASE - 忽略大小写
    result1 = re.findall(r"hello", text, re.IGNORECASE)
    print("忽略大小写:", result1)  # ['Hello']
    
    # re.MULTILINE - 多行模式
    result2 = re.findall(r"^.+", text, re.MULTILINE)
    print("多行模式:", result2)  # ['Hello', 'World', 'Python']
    
    # re.DOTALL - 让 . 匹配包括换行符在内的所有字符
    result3 = re.findall(r"Hello.*Python", text, re.DOTALL)
    print("DOTALL模式:", result3)  # ['Hello\nWorld\nPython']
    
    # 组合使用多个标志
    result4 = re.findall(r"^[a-z]+", text, re.IGNORECASE | re.MULTILINE)
    print("组合标志:", result4)  # ['Hello', 'World', 'Python']

regex_flags()

7. 实际应用示例

邮箱验证

def validate_email(email):
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))

# 测试邮箱验证
emails = [
    "user@example.com",
    "user.name@domain.co.uk", 
    "invalid-email",
    "user@domain",
    "user@.com"
]

for email in emails:
    print(f"{email}: {validate_email(email)}")

电话号码提取

def extract_phone_numbers(text):
    # 匹配多种电话号码格式
    pattern = r'''
        (?:\+86\s?)?              # 可选国际码
        (?:1[3-9]\d{9})           # 手机号
        |                          # 或
        (?:\d{3,4}-)?\d{7,8}      # 座机号
    '''
    
    phones = re.findall(pattern, text, re.VERBOSE)
    return phones

text = """
联系方式:
手机:13812345678,13587654321
电话:010-12345678,021-87654321
国际:+86 13900001111
"""

phones = extract_phone_numbers(text)
print("提取的电话号码:", phones)

URL解析

def parse_url(url):
    pattern = r'''
        ^
        (https?)://          # 协议
        ([^/:]+)             # 域名
        (?:[:](\d+))?        # 端口(可选)
        (/.*)?               # 路径(可选)
        $
    '''
    
    match = re.match(pattern, url, re.VERBOSE)
    if match:
        return {
            'protocol': match.group(1),
            'domain': match.group(2),
            'port': match.group(3) or '80',
            'path': match.group(4) or '/'
        }
    return None

urls = [
    "https://www.example.com",
    "http://localhost:8080/api/v1/users",
    "https://example.com:443/path/to/page"
]

for url in urls:
    parsed = parse_url(url)
    print(f"{url} -> {parsed}")

HTML内容提取

def extract_html_content(html):
    # 提取所有链接
    links = re.findall(r'<a[^>]*href="([^"]*)"[^>]*>', html)
    
    # 提取所有图片
    images = re.findall(r'<img[^>]*src="([^"]*)"[^>]*>', html)
    
    # 提取文本内容(简单版本)
    text = re.sub(r'<[^>]+>', '', html)
    text = re.sub(r'\s+', ' ', text).strip()
    
    return {
        'links': links,
        'images': images,
        'text': text
    }

html_content = """
<div>
    <h1>标题</h1>
    <p>这是一个<a href="https://example.com">链接</a></p>
    <img src="image.jpg" alt="图片">
    <p>另一个<a href="/page.html">页面</a></p>
</div>
"""

result = extract_html_content(html_content)
print("HTML解析结果:", result)

8. 调试和测试技巧

def debug_regex():
    pattern = r"(\d{3})-(\d{4})-(\d{4})"
    text = "电话:010-1234-5678"
    
    match = re.search(pattern, text)
    if match:
        print("完整匹配:", match.group(0))
        print("分组:", match.groups())
        print("分组字典:", match.groupdict())
        print("匹配位置:", match.span())
        
        # 详细调试信息
        print("\n调试信息:")
        for i, group in enumerate(match.groups(), 1):
            print(f"  分组{i}: '{group}' (位置: {match.span(i)})")

debug_regex()

9. 性能优化建议

def optimize_regex():
    import time
    
    text = "a" * 1000 + "b"
    
    # 不好的写法 - 灾难性回溯
    start = time.time()
    try:
        re.match(r"(a+)+b", text)
    except:
        pass
    print(f"灾难性回溯耗时: {time.time() - start:.4f}s")
    
    # 好的写法
    start = time.time()
    re.match(r"a+b", text)
    print(f"优化后耗时: {time.time() - start:.4f}s")
    
    # 使用编译后的正则表达式
    pattern = re.compile(r"a+b")
    start = time.time()
    for _ in range(1000):
        pattern.match(text)
    print(f"编译后1000次匹配: {time.time() - start:.4f}s")

optimize_regex()

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值