正则表达式完全指南:从入门到精通

正则表达式完全指南:从入门到精通

一、什么是正则表达式?

正则表达式(Regular Expression,简称 Regex)是一种用于匹配字符串中字符组合的模式。它由普通字符和特殊字符(元字符)组成,可以用来验证、搜索、替换文本。

🎯 核心价值:用一行代码完成复杂的字符串处理任务


二、基础语法

2.1 普通字符

字符 说明 示例
abc 匹配字符串 "abc" hello 匹配 hello
123 匹配数字 123abc 匹配 123
\ 转义字符 \. 匹配实际的点号

2.2 元字符(Metacharacters)

元字符 说明 示例 匹配结果
. 匹配任意单个字符(除换行符) h.t hat, hot, h t
^ 匹配字符串开头 ^Hello 以 Hello 开头的字符串
$ 匹配字符串结尾 world$ 以 world 结尾的字符串
` ` 或运算符 `cat
() 分组捕获 (ab)+ 匹配一个或多个 ab
[] 字符集 [abc] 匹配 a、b 或 c

2.3 量词(Quantifiers)

量词 说明 示例 匹配次数
* 零次或多次 ab*c ac, abc, abbc...
+ 一次或多次 ab+c abc, abbc...(不匹配 ac)
? 零次或一次 colou?r color, colour
{n} 恰好 n 次 a{3} aaa
{n,} 至少 n 次 a{2,} aa, aaa, aaaa...
{n,m} n 到 m 次 a{2,4} aa, aaa, aaaa

2.4 字符类(Character Classes)

说明 示例
\d 数字 0-9 \d+ 匹配 123
\D 非数字 \D+ 匹配 abc
\w 单词字符 a-zA-Z0-9_ \w+ 匹配 hello_123
\W 非单词字符 \W+ 匹配 @#$
\s 空白字符(空格、制表符、换行符) \s+ 匹配多个空格
\S 非空白字符 \S+ 匹配 hello
\b 单词边界 \bword\b 精确匹配 word
\B 非单词边界 \Bword\B 匹配 swordfish 中的 word

2.5 反义(Negations)

说明 示例
[^abc] 不匹配 a、b、c [^0-9] 匹配非数字
[a-z] 匹配小写字母 [a-z]+ 匹配 hello
[A-Z] 匹配大写字母 [A-Z]+ 匹配 HELLO
[0-9] 匹配数字 [0-9]+ 匹配 12345

三、进阶语法

3.1 贪婪与非贪婪

模式 说明 示例
.* 贪婪匹配(尽可能多) .* in aabbcc 匹配 aabbcc
.*? 非贪婪匹配(尽可能少) .*? in aabbcc 匹配空字符串
.+? 非贪婪匹配 .+? in aabbcc 匹配 a
{n,m}? 非贪婪量词 a{2,4}? 匹配 aa

示例:

python 复制代码
import re

html = '<p>Hello</p><p>World</p>'

# 贪婪匹配(错误)
re.findall(r'<p>.*</p>', html)  # ['<p>Hello</p><p>World</p>']

# 非贪婪匹配(正确)
re.findall(r'<p>.*?</p>', html)  # ['<p>Hello</p>', '<p>World</p>']

3.2 前瞻与后顾(Lookaround)

类型 语法 说明
正向前瞻 (?=pattern) 匹配后面跟着 pattern 的位置
负向前瞻 (?!pattern) 匹配后面不跟着 pattern 的位置
正向后顾 (?<=pattern) 匹配前面是 pattern 的位置
负向后顾 (?<!pattern) 匹配前面不是 pattern 的位置

示例:

python 复制代码
import re

# 提取价格(不包含货币符号)
text = '价格:$100 和 ¥200'

# 正向前瞻
re.findall(r'(?<=\$)\d+', text)   # ['100']
re.findall(r'(?<=¥)\d+', text)    # ['200']

# 负向前瞻:匹配后面不跟数字的单词
re.findall(r'\b\w+(?!\d)', 'abc123 def456')  # ['abc', 'def']

3.3 分组与捕获

python 复制代码
import re

# 基本分组
pattern = r'(\d{3})-(\d{4})'
text = '电话:123-4567'
match = re.search(pattern, text)

if match:
    print(match.group(0))  # 123-4567(完整匹配)
    print(match.group(1))  # 123(第一组)
    print(match.group(2))  # 4567(第二组)
    print(match.groups())   # ('123', '4567')

3.4 命名分组

python 复制代码
import re

# 命名分组 (?P<name>pattern)
pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
text = '日期:2024-01-15'

match = re.search(pattern, text)
if match:
    print(match.group('year'))   # 2024
    print(match.group('month'))  # 01
    print(match.group('day'))    # 15

3.5 非捕获分组

python 复制代码
import re

# 非捕获分组 (?:pattern) - 不保存匹配结果
pattern = r'(?:http|https)://(\S+)'
text = '访问 https://example.com'

match = re.search(pattern, text)
if match:
    print(match.group(0))  # https://example.com
    print(match.group(1))  # example.com(只有捕获组)

四、Python re 模块详解

4.1 常用函数

函数 说明 返回值
re.match() 从字符串开头匹配 Match 对象或 None
re.search() 搜索整个字符串 第一个匹配的 Match 对象
re.findall() 查找所有匹配 列表
re.finditer() 查找所有匹配 迭代器
re.sub() 替换匹配的字符串 新字符串
re.split() 按模式分割字符串 列表
re.compile() 编译正则表达式 Pattern 对象

4.2 使用示例

python 复制代码
import re

# 1. match - 从开头匹配
re.match(r'\d+', '123abc')    # Match: '123'
re.match(r'\d+', 'abc123')    # None(不是开头)

# 2. search - 搜索第一个匹配
re.search(r'\d+', 'abc123def')  # Match: '123'

# 3. findall - 查找所有匹配
re.findall(r'\d+', 'a1b2c3')   # ['1', '2', '3']

# 4. sub - 替换
re.sub(r'\d+', '*', 'a1b2c3')  # 'a*b*c*'
re.sub(r'(\w+)', r'\1\1', 'ab')  # 'aabb'(重复单词)

# 5. split - 分割
re.split(r'\s+', 'hello world foo')  # ['hello', 'world', 'foo']
re.split(r'[;|,]', 'a;b,c|d')       # ['a', 'b', 'c', 'd']

# 6. compile - 编译复用
email_pattern = re.compile(r'[\w.]+@[\w.]+\.\w+')
email_pattern.findall('test@example.com and info@test.org')
# ['test@example.com', 'info@test.org']

4.3 编译标志(Flags)

标志 说明 使用方式
re.IGNORECASE 忽略大小写 re.IGNORECASEre.I
re.MULTILINE 多行模式(^ $ 匹配每行) re.MULTILINEre.M
re.DOTALL 点号匹配换行符 re.DOTALLre.S
re.VERBOSE 允许注释和空格 re.VERBOSEre.X
re.UNICODE Unicode 模式 re.UNICODEre.U
python 复制代码
import re

# 忽略大小写
re.findall(r'hello', 'Hello HELLO hello', re.I)  # ['Hello', 'HELLO', 'hello']

# 多行模式
text = 'line1\nline2\nline3'
re.findall(r'^\w+', text, re.M)  # ['line1', 'line2', 'line3']

# 点号匹配换行符
re.findall(r'.+', 'line1\nline2', re.S)  # ['line1\nline2']

# 详细的正则(忽略空格和注释)
phone = re.compile(r'''
    (\d{3,4})     # 区号
    [-\s]?        # 可选分隔符
    (\d{7,8})     # 电话号码
''', re.VERBOSE)

五、爬虫实战常用正则

5.1 URL 提取

python 复制代码
import re

# 匹配 HTTP/HTTPS URL
url_pattern = r'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+[^\s]*'
text = '请访问 https://example.com/path?query=1 或 http://test.org'
re.findall(url_pattern, text)
# ['https://example.com/path?query=1', 'http://test.org']

# 匹配相对 URL
relative_pattern = r'href=["\']([^"\']+)["\']'
html = '<a href="/page1">Link</a>'
re.findall(relative_pattern, html)  # ['/page1']

5.2 邮箱提取

python 复制代码
import re

# 基本邮箱验证
email_pattern = r'[\w.+-]+@[\w-]+\.[\w.-]+'

# 更严格的邮箱验证
strict_email = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

emails = """
联系方式:
- 简单邮箱:test@example.com
- 公司邮箱:user.name@company.co.uk
- 个人邮箱:user+tag@gmail.com
"""
re.findall(email_pattern, emails)
# ['test@example.com', 'user.name@company.co.uk', 'user+tag@gmail.com']

5.3 手机号提取

python 复制代码
import re

# 中国大陆手机号
phone_pattern = r'1[3-9]\d{9}'
# 带格式的手机号
formatted_phone = r'1[3-9]\d{1}[-\s]?\d{4}[-\s]?\d{4}'

text = '联系我:13812345678 或 139-1234-5678'
re.findall(phone_pattern, text)  # ['13812345678', '13912345678']

5.4 IP 地址提取

python 复制代码
import re

# 基本 IPv4
ip_pattern = r'\b(?:\d{1,3}\.){3}\d{1,3}\b'

# 严格的 IPv4 验证
strict_ip = r'^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$'

log = '访问来自 192.168.1.100 和 10.0.0.1'
re.findall(ip_pattern, log)  # ['192.168.1.100', '10.0.0.1']

5.5 HTML 内容提取

python 复制代码
import re

# 提取标题
title_pattern = r'<title>(.*?)</title>'

# 提取 meta 描述
meta_pattern = r'<meta\s+name=["\']description["\']\s+content=["\'](.*?)["\']'

# 提取所有文本(去标签)
text_pattern = r'<[^>]+>'

html = '''
<html>
<head><title>网页标题</title></head>
<body>
  <h1>主标题</h1>
  <p>段落内容</p>
</body>
</html>
'''

re.findall(title_pattern, html)  # ['网页标题']
re.sub(text_pattern, '', html)   # '网页标题主标题段落内容'

5.6 数据清洗

python 复制代码
import re

# 去除多余空白
clean = r'\s+'
text = '  hello   world  \n  foo  '
re.sub(clean, ' ', text.strip())  # 'hello world foo'

# 去除 HTML 实体
entity_pattern = r'&[a-zA-Z]+;|&#\d+;'
text = '价格:100&lt;200 &amp; 300'
re.sub(entity_pattern, lambda m: {
    '&lt;': '<', '&gt;': '>', '&amp;': '&'
}.get(m.group(), ''), text)

# 中文提取
chinese_pattern = r'[\u4e00-\u9fa5]+'
text = 'Hello 你好 World 世界'
re.findall(chinese_pattern, text)  # ['你好', '世界']

六、常用正则表达式速查表

6.1 验证类

python 复制代码
# 手机号验证
r'^1[3-9]\d{9}$'

# 邮箱验证
r'^[\w.+-]+@[\w-]+\.[\w.-]+$'

# 身份证号验证(18位)
r'^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$'

# URL 验证
r'^https?://[-\w.]+(?:/[-\w./]*)?$'

# IP 地址验证
r'^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$'

6.2 提取类

python 复制代码
# 中文字符
r'[\u4e00-\u9fa5]+'

# 英文单词
r'\b[a-zA-Z]+\b'

# 数字(整数和小数)
r'-?\d+\.?\d*'

# 日期格式 YYYY-MM-DD
r'\d{4}[-/]\d{1,2}[-/]\d{1,2}'

# 时间格式 HH:MM:SS
r'\d{1,2}:\d{2}:\d{2}'

# Hex 颜色值
r'#(?:[0-9a-fA-F]{3}){1,2}\b'

6.3 替换类

python 复制代码
# 压缩多个空格
r'\s+'

# HTML 标签
r'<[^>]+>'

# 中文标点转英文标点
r'[,。;:!?]'

# 全角转半角
r'[\uff01-\uff5e]'

七、性能优化技巧

7.1 编译正则

python 复制代码
import re

# ❌ 不推荐:每次调用都重新编译
def find_emails(text):
    return re.findall(r'[\w.+-]+@[\w-]+\.[\w.-]+', text)

# ✅ 推荐:编译一次复用
EMAIL_PATTERN = re.compile(r'[\w.+-]+@[\w-]+\.[\w.-]+')
def find_emails(text):
    return EMAIL_PATTERN.findall(text)

7.2 使用原始字符串

python 复制代码
# ❌ 不推荐:需要双重转义
pattern = "\\d+\\.\\d+"

# ✅ 推荐:原始字符串
pattern = r'\d+\.\d+'

7.3 避免回溯

python 复制代码
import re

# ❌ 容易产生回溯(灾难性回溯)
bad_pattern = r'(a+)+b'

# ✅ 更高效的写法
good_pattern = r'a+b'

7.4 使用非贪婪匹配

python 复制代码
import re

html = '<div>content</div><div>more</div>'

# ❌ 贪婪匹配
re.findall(r'<div>.*</div>', html)  # ['<div>content</div><div>more</div>']

# ✅ 非贪婪匹配
re.findall(r'<div>.*?</div>', html)  # ['<div>content</div>', '<div>more</div>']

八、常见错误与调试

8.1 常见错误

错误 原因 解决方案
re.error: nothing to repeat 量词前没有字符 检查正则表达式
re.error: bad escape 无效的转义序列 使用原始字符串
匹配结果为空 正则写错或模式不匹配 逐步调试正则

8.2 调试技巧

python 复制代码
import re

# 使用 re.VERBOSE 添加注释
pattern = re.compile(r'''
    ^                   # 字符串开头
    (?P<year>\d{4})     # 年
    -                   # 分隔符
    (?P<month>\d{2})    # 月
    -                   # 分隔符
    (?P<day>\d{2})      # 日
    $                   # 字符串结尾
''', re.VERBOSE)

# 在线调试工具
# https://regex101.com/
# https://debuggex.com/

九、总结

📋 正则表达式核心要点

  1. 基础元字符., ^, $, *, +, ?, [], (), \|
  2. 字符类\d, \w, \s 及其大写反义
  3. 量词*, +, ?, {n}, {n,}, {n,m}
  4. 贪婪与非贪婪.* vs .*?
  5. 分组捕获() 和命名分组 (?P<name>)
  6. 前瞻后顾(?=), (?!), (?<=), (?<!)

🎯 学习建议

  1. 从简单模式开始,逐步复杂化
  2. 使用在线工具(regex101.com)测试
  3. 先理解需求,再编写正则
  4. 避免过度使用,可读性很重要
  5. 注意性能,避免灾难性回溯
相关推荐
tianyu2342 天前
Java 正则表达式终极指南:从 Pattern 到 Matcher,从双重转义到性能优化,一篇全搞定
java·开发语言·正则表达式·group·find·pattern·matcher
OPEN-F4 天前
Python进阶教程:正则表达式进阶与文本处理
开发语言·python·正则表达式
小小龙学IT7 天前
C++ 正则表达式完全指南:从 std::regex 实战到 RE2 引擎原理(NFA/DFA/回溯陷阱)
c++·正则表达式
傻啦嘿哟7 天前
猫眼电影TOP100爬虫:Requests+正则表达式实战
爬虫·正则表达式
circuitsosk7 天前
AI输出的“质检员”:构建智能体质量评估、异常检测与人工兜底的三层防线
人工智能·python·microsoft·正则表达式·langchain
Livia要学习7 天前
Python正则表达式
正则表达式
wangchen_09 天前
C++正则表达式
开发语言·c++·正则表达式
Patrick在香港10 天前
Python正则表达式实战:解析和验证香港身份证、车牌、电话格式
开发语言·python·正则表达式
吴声子夜歌12 天前
正则指引——Ruby
开发语言·正则表达式·ruby