正则表达式入门教程(保姆级教程)

正则表达式入门教程:从零开始掌握文本匹配的艺术

你有没有遇到过这样的场景?需要从一段长长的日志中提取所有错误代码,或者验证用户输入的邮箱是否合法,又或者批量替换文本中的某个模式。这些看似琐碎的任务,其实都可以通过一种强大的工具轻松解决——正则表达式。

正则表达式,简称 regex,是程序员处理文本时的“瑞士军刀”。它不是一门编程语言,而是一种模式描述语言,用来定义字符串的匹配规则。无论你是初学者还是有一定经验的开发者,只要掌握它的基本语法,就能大幅提升文本处理效率。

本文将带你从零开始,逐步构建对正则表达式的理解。我们将从最基础的字符匹配讲起,逐步深入到分组、捕获、替换等高级用法,并结合真实项目案例,让你真正“会用”而不是“背规则”。


什么是正则表达式?它能做什么?

简单来说,正则表达式就是一种用来描述字符串模式的表达式。你可以把它想象成一个“智能搜索模板”:不是单纯找某个固定的字,而是告诉系统“我想要找什么样的结构”。

举个例子:
你有一份用户注册数据,其中包含邮箱字段。你想找出所有格式正确的邮箱地址。手动一个个检查显然不现实,但用正则表达式,只需一行代码就能完成。

在 Python 中,我们可以这样写:

import re

email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

test_emails = [
    "user@example.com",
    "admin@company.org",
    "invalid.email",
    "test@sub.domain.co.uk"
]

for email in test_emails:
    if re.match(email_pattern, email):
        print(f"✅ {email} 是合法邮箱")
    else:
        print(f"❌ {email} 格式不正确")

注释说明

  • r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' 是正则表达式模式
  • ^ 表示字符串开始,$ 表示字符串结束,确保整个字符串都符合规则
  • [a-zA-Z0-9._%+-]+ 匹配用户名部分,允许字母、数字和常见特殊字符
  • @ 是必须存在的符号
  • [a-zA-Z0-9.-]+ 匹配域名部分
  • \. 匹配点号(因为点在正则中有特殊含义,需转义)
  • {2,} 表示至少两个字母,用于匹配顶级域名如 .com.org
  • re.match() 从头开始匹配,返回匹配对象或 None

基础字符匹配:从最简单的开始

正则表达式的核心是“匹配”。我们先从最基础的字符匹配入手。

字面量匹配

最简单的正则就是直接写你要找的字符。比如,要找字符串中的 "hello":

import re

text = "Hello world, this is a hello test."
match = re.search(r'hello', text, re.IGNORECASE)

if match:
    print(f"找到匹配项:{match.group()}")
else:
    print("未找到匹配")

注释说明

  • r'hello' 是正则表达式,表示精确匹配字符 "hello"
  • re.IGNORECASE 参数让匹配不区分大小写,因此 "Hello" 也能被匹配到
  • re.search() 在整个字符串中查找第一个匹配项,返回匹配对象
  • .group() 提取实际匹配到的文本

特殊字符与转义

正则表达式中有一些字符具有特殊含义,比如 .^$*+? 等。如果你真的想匹配这些字符本身,就需要用反斜杠 \ 转义。

例如,要匹配实际的点号:

import re

text = "文件名:data.json"
pattern = r'\.'

match = re.search(pattern, text)
if match:
    print(f"找到了点号:{match.group()}")

注释说明

  • \. 表示匹配一个字面量的点号
  • 若不转义,. 会匹配任意单个字符,导致错误匹配

元字符与量词:掌握匹配的“节奏”

元字符是正则表达式中的“控制键”,它们决定了匹配的行为和范围。

常见元字符速查表

元字符 含义 示例
. 匹配任意单个字符(换行符除外) a.c 可匹配 "abc"、"a2c"
^ 匹配字符串开头 ^Hello 只匹配以 "Hello" 开头的字符串
$ 匹配字符串结尾 world$ 只匹配以 "world" 结尾的字符串
* 匹配前一个字符 0 次或多次 ab*c 可匹配 "ac"、"abc"、"abbc"
+ 匹配前一个字符 1 次或多次 ab+c 可匹配 "abc"、"abbc",但不匹配 "ac"
? 匹配前一个字符 0 次或 1 次 colou?r 可匹配 "color" 或 "colour"
{n} 精确匹配 n 次 a{3} 只匹配 "aaa"
{n,m} 匹配 n 到 m 次 a{2,4} 可匹配 "aa"、"aaa"、"aaaa"

实际案例:手机号验证

我们来写一个正则表达式,验证中国大陆手机号格式(11位数字,以 1 开头,第二位为 3-9):

import re

phone_pattern = r'^1[3-9]\d{9}$'

test_phones = [
    "13812345678",  # 合法
    "12345678901",  # 非法(第二位不是3-9)
    "1381234567",   # 非法(不足11位)
    "18912345678"   # 合法
]

for phone in test_phones:
    if re.match(phone_pattern, phone):
        print(f"✅ {phone} 是有效手机号")
    else:
        print(f"❌ {phone} 格式错误")

注释说明

  • ^1[3-9] 表示以 1 开头,第二位是 3 到 9 的数字
  • \d{9} 表示后面必须有 9 个数字
  • $ 确保字符串结束,防止多出字符
  • 整体保证是 11 位合法手机号

字符类与预定义字符集:快速匹配一组字符

当你需要匹配多个可能的字符时,使用字符类(Character Class)会更高效。

字符类语法

  • [abc]:匹配 a、b 或 c 中任意一个
  • [a-z]:匹配任意小写字母
  • [A-Z]:匹配任意大写字母
  • [0-9]:匹配任意数字
  • [^abc]:匹配非 a、b、c 的字符(取反)

实例:提取数字

假设你有一段文本,需要提取所有数字:

import re

text = "订单号:20240512,金额:128.50元,发货时间:2024-05-13"

numbers = re.findall(r'\d+', text)

print(f"提取到的数字:{numbers}")

注释说明

  • \d 是预定义字符类,等价于 [0-9]
  • + 表示一个或多个数字
  • re.findall() 返回所有匹配项组成的列表,无需循环

分组与捕获:让正则更智能

分组是正则表达式中非常强大的功能。通过括号 (),你可以将一部分表达式当作一个整体处理。

基本分组

import re

text = "张三,电话:13812345678,邮箱:zhangsan@example.com"

pattern = r'电话:(\d{11}),邮箱:([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})'

match = re.search(pattern, text)

if match:
    phone = match.group(1)     # 第一个分组:电话
    email = match.group(2)     # 第二个分组:邮箱
    print(f"电话:{phone}")
    print(f"邮箱:{email}")

注释说明

  • (\d{11}) 是第一个分组,用于捕获手机号
  • ([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}) 是第二个分组,用于捕获邮箱
  • group(1)group(2) 分别获取两个分组的内容
  • 分组编号从 1 开始,group(0) 是完整匹配的文本

替换与高级技巧:从匹配到处理

正则表达式不仅用于查找,还能用于替换。re.sub() 函数可以实现文本替换。

案例:清洗日志中的时间戳

日志中时间戳格式为 [2024-05-13 12:34:56],我们想将其替换为更简洁的格式:

import re

log_line = "[2024-05-13 12:34:56] 用户登录失败,IP:192.168.1.1"

cleaned = re.sub(r'\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]', '【时间】', log_line)

print(cleaned)

注释说明

  • \d{4} 匹配4位年份
  • \d{2} 匹配两位月、日、时、分、秒
  • 整个时间戳被 [] 包裹,用 \[\] 转义
  • re.sub() 第一个参数是正则模式,第二个是替换内容,第三个是原字符串

总结:正则表达式入门教程的最后建议

正则表达式虽然强大,但初学者容易陷入“写得太复杂”或“误匹配”的陷阱。记住几个核心原则:

  1. 从小处着手:先从简单匹配开始,逐步叠加规则。
  2. 善用工具:使用在线正则测试工具(如 regex101.com)验证你的表达式。
  3. 测试多场景:确保边界情况(如空值、极端长度)也能正确处理。
  4. 保持可读性:复杂正则可用 re.VERBOSE 模式拆分行注释。

掌握正则表达式,就像学会了“文本世界的语言”。它能让你在日志分析、数据清洗、表单验证等场景中游刃有余。希望这篇正则表达式入门教程,能成为你通往高效编程之路的第一步。