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

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

一、什么是正则表达式?

正则表达式(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. 注意性能,避免灾难性回溯
相关推荐
yume_sibai5 天前
正则表达式完全指南(基础语法 + 实战应用 + 性能优化 + 最佳实践)
开发语言·正则表达式·c#
研☆香6 天前
js中使用的正则表达式
开发语言·javascript·正则表达式
2601_9622935311 天前
浅学python | 正则表达式集锦
正则表达式·字符串匹配·python知识·浅学python·ip地址检查
2601_9622955811 天前
Python中正则表达式的知识汇总分享
python·正则表达式·模块·语法·匹配
启观川11 天前
Python基础-第 16 章 综合案例:客户信息管理系统
开发语言·笔记·python·正则表达式
2601_9622940511 天前
Python 操作 Word:如何查找、替换和批量修改文本
python·正则表达式·word·文档处理·查找替换
for_ever_love__16 天前
python基础语法学习: 正则表达式
python·学习·正则表达式
asdfg125896317 天前
一个例子通俗理解Java中正则表达式用法
java·正则表达式
Tanner_SL18 天前
Linux笔记之正则表达式和文本处理grep,sed,awk命令
linux·正则表达式
谢亮_vipxieliang19 天前
手机号验证技术方案:号段规则与正则表达式
java·服务器·spring boot·正则表达式·hibernate