指南

正则表达式完全指南:语法、常用模式与实战技巧

系统讲解正则表达式的核心语法、元字符、量词、分组、断言及常用匹配模式。附带大量实用示例,助你快速掌握文本匹配与处理的利器。

正则表达式(Regular Expression,简称 Regex 或 RegExp)是一种用于描述文本模式的微型语言。无论你是做表单验证、日志分析、文本搜索替换,还是数据清洗,正则表达式都是不可或缺的工具。

虽然正则表达式的语法初看起来令人望而生畏,但只要理解了核心概念,就能很快应用到实际工作中。本文将带你从零开始,系统地掌握正则表达式。

如果你想即时测试正则表达式,推荐使用我们的 在线正则表达式测试工具,它可以实时高亮匹配结果,帮助你快速调试表达式。

1. 什么是正则表达式?

正则表达式是一个由普通字符和特殊字符(元字符)组成的字符串,用于定义一个搜索模式。这个模式可以用来:

  • 匹配:判断一段文本是否符合某个模式
  • 搜索:在文本中查找所有符合模式的子串
  • 替换:将匹配的部分替换为新内容
  • 提取:从文本中提取特定格式的数据

正则表达式广泛应用于几乎所有编程语言(JavaScript、Python、Java、Go、PHP 等)以及很多命令行工具(grep、sed、awk)。

2. 基础语法

2.1 普通字符

大多数字母和数字在正则中表示它们本身。例如正则 hello 就是匹配字符串中的文本 “hello”。

2.2 元字符

元字符是正则表达式中具有特殊含义的字符,它们是正则表达式的核心。

元字符含义示例
.匹配除换行符 \n 之外的任意单个字符a.c 匹配 “abc”、“a1c”、“a-c”
\转义字符,将元字符转义为普通字符\. 匹配字面量 ”.”
^匹配字符串的开头(或多行模式下每行的开头)^Hello 匹配以 “Hello” 开头的行
$匹配字符串的结尾(或多行模式下每行的结尾)world$ 匹配以 “world” 结尾的行
``逻辑”或”,匹配左右两侧任意一个表达式

2.3 字符类(Character Classes)

字符类使用方括号 [] 定义,表示匹配其中任意一个字符。

表达式含义示例
[abc]匹配 a、b 或 c 中的任意一个[aeiou] 匹配任意一个小写元音
[a-z]匹配从 a 到 z 的任意一个小写字母[A-Za-z] 匹配任意一个英文字母
[0-9]匹配任意一个数字[0-9] 在多数常见场景下等价于 \d
[^abc]取反,匹配不在括号内的任意字符[^0-9] 匹配任意非数字字符

注意:在字符类内部,大多数元字符会失去其特殊含义。例如 [.] 匹配的是字面量 ”.”,而非任意字符。但 \]^(仅在首位时)和 -(在中间时)仍有特殊含义。

补充:在某些正则引擎的 Unicode 模式下,\d 可能匹配更多数字字符(如其他语言的数字),而 [0-9] 仅匹配 ASCII 数字 0–9。

2.4 预定义字符类

为了方便,正则表达式提供了常用字符类的简写形式。

简写等价的字符类含义
\d[0-9]匹配任意数字
\D[^0-9]匹配任意非数字字符
\w[A-Za-z0-9_]匹配任意”词汇字符”(字母、数字、下划线)
\W[^A-Za-z0-9_]匹配任意非词汇字符
\s[ \t\n\r\f\v]匹配任意空白字符(空格、制表符、换行等)
\S[^ \t\n\r\f\v]匹配任意非空白字符
\b匹配单词边界
\B匹配非单词边界

3. 量词(Quantifiers)

量词用于指定前面的字符或分组需要出现的次数。

量词含义示例
*匹配 0 次或多次ab*c 匹配 “ac”、“abc”、“abbc”
+匹配 1 次或多次ab+c 匹配 “abc”、“abbc”,但不匹配 “ac”
?匹配 0 次或 1 次colou?r 匹配 “color” 和 “colour”
{n}精确匹配 n 次\d{4} 匹配恰好 4 位数字
{n,}匹配至少 n 次\d{2,} 匹配 2 位或更多位数字
{n,m}匹配至少 n 次,至多 m 次\d{2,4} 匹配 2 到 4 位数字

3.1 贪婪与非贪婪

默认情况下,量词是贪婪的——它会尽可能多地匹配字符。在量词后加一个 ? 即可变为非贪婪(惰性)模式,尽可能少地匹配。

贪婪模式:  <.+>   对 "<em>hello</em>" 匹配整个 "<em>hello</em>"
非贪婪模式:<.+?>  对 "<em>hello</em>" 匹配 "<em>" 和 "</em>"

这在处理 HTML 标签等场景时非常关键。

4. 分组与捕获

4.1 捕获分组 ()

圆括号 () 用于将多个字符组合为一个逻辑单元,并捕获匹配的内容以供后续使用。

(abc)+      匹配一个或多个连续的 "abc"
(\d{4})-(\d{2})-(\d{2})   匹配日期格式,分别捕获年、月、日

在替换中,可以通过 $1$2(或 \1\2,取决于语言)引用捕获组的内容。

4.2 非捕获分组 (?:)

有时你只需要分组但不需要捕获,可以使用 (?:) 来避免不必要的性能开销。

(?:https?|ftp)://    分组但不捕获协议部分

4.3 命名分组 (?<name>)

一些正则引擎支持命名捕获组,使捕获结果更具可读性。

(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})

可通过 $<year> 或对应语言的 API 来引用。

5. 断言(Assertions)

断言用于指定匹配位置的条件,但不消耗字符(零宽度匹配)。

5.1 锚点断言

  • ^:匹配字符串开头
  • $:匹配字符串结尾
  • \b:匹配单词边界
\bcat\b    只匹配完整单词 "cat",不匹配 "category" 中的 "cat"

5.2 前瞻断言(Lookahead)

语法名称含义
(?=pattern)正向前瞻匹配后面紧跟 pattern 的位置
(?!pattern)负向前瞻匹配后面不紧跟 pattern 的位置
\d+(?=元)    匹配后面跟着"元"的数字,如 "100元" 中的 "100"
foo(?!bar)   匹配后面不是 "bar" 的 "foo"

5.3 后瞻断言(Lookbehind)

语法名称含义
(?<=pattern)正向后瞻匹配前面紧接 pattern 的位置
(?<!pattern)负向后瞻匹配前面不紧接 pattern 的位置
(?<=\$)\d+    匹配前面是 "$" 的数字,如 "$99" 中的 "99"
(?<!\\)\"     匹配前面没有反斜杠的双引号

注意:后瞻断言并非所有正则引擎都支持。JavaScript 从 ES2018 开始支持;Python 和 Java 均支持。部分引擎要求后瞻中的 pattern 长度固定。

6. 修饰符(Flags)

修饰符用于改变正则表达式的匹配行为。

修饰符名称含义
i忽略大小写(Case-Insensitive)匹配时不区分大小写
g全局匹配(Global)查找所有匹配项,而不是找到第一个就停止
m多行模式(Multiline)使 ^$ 分别匹配每行的开头和结尾,而非整个字符串
s单行模式(DotAll)使 . 也能匹配换行符 \n
uUnicode 模式启用完整的 Unicode 匹配

在不同语言中,修饰符的使用方式略有不同:

// JavaScript
/pattern/gi

// Python
re.compile(r'pattern', re.IGNORECASE | re.MULTILINE)

7. 常用正则表达式模式

以下是一些经过验证的常用正则表达式。请注意,实际使用时应根据具体场景进行调整。

7.1 电子邮箱验证

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

说明:匹配标准电子邮箱格式。用户名部分允许字母、数字、点号、下划线、百分号、加号和连字符;域名部分允许字母、数字、点号和连字符;顶级域名至少 2 个字母。

提示:完全符合 RFC 5322 标准的邮箱正则表达式极为复杂。上述表达式适用于大多数常见场景,但如果需要严格验证,建议结合后端验证。

7.2 URL 匹配

https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)

说明:匹配以 http://https:// 开头的 URL。

7.3 中国大陆手机号

^1[3-9]\d{9}$

说明:以数字 1 开头,第二位为 3-9,后面跟 9 位数字,总共 11 位。

7.4 身份证号码(中国大陆 18 位)

^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$

说明:匹配 18 位身份证号。依次为 6 位地区码、8 位出生日期(年月日)、3 位顺序码和 1 位校验码(数字或 X)。

7.5 IPv4 地址

^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$

说明:匹配标准 IPv4 地址,每段范围为 0-255。

7.6 强密码验证

^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$

说明:要求至少 8 个字符,且包含至少一个小写字母、一个大写字母、一个数字和一个特殊字符。

7.7 日期格式(YYYY-MM-DD)

^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$

说明:匹配 YYYY-MM-DD 格式的日期。注意此正则不会验证日期的逻辑有效性(例如 2 月 30 日会通过匹配)。

7.8 十六进制颜色值

^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$

说明:匹配 3 位或 6 位十六进制颜色值,如 #FFF#FF5733

8. 各语言中的正则表达式

8.1 JavaScript

// 创建正则表达式
const regex = /\d+/g;
// 或者
const regex2 = new RegExp('\\d+', 'g');

// 测试匹配
regex.test('hello 123');          // true

// 查找匹配
'hello 123 world 456'.match(/\d+/g);  // ['123', '456']

// 替换
'hello 123'.replace(/\d+/, '***');     // 'hello ***'

// 捕获组
const match = '2026-04-19'.match(/(\d{4})-(\d{2})-(\d{2})/);
// match[1] = '2026', match[2] = '04', match[3] = '19'

8.2 Python

import re

# 测试匹配
re.search(r'\d+', 'hello 123')       # 返回 Match 对象

# 查找所有匹配
re.findall(r'\d+', 'hello 123 world 456')   # ['123', '456']

# 替换
re.sub(r'\d+', '***', 'hello 123')         # 'hello ***'

# 捕获组
match = re.match(r'(\d{4})-(\d{2})-(\d{2})', '2026-04-19')
match.group(1)  # '2026'
match.group(2)  # '04'
match.group(3)  # '19'

# 命名分组
match = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', '2026-04-19')
match.group('year')  # '2026'

8.3 Java

import java.util.regex.*;

// 编译正则表达式
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("hello 123 world 456");

// 查找所有匹配
while (matcher.find()) {
    System.out.println(matcher.group());  // 输出 "123",然后 "456"
}

// 替换
String result = "hello 123".replaceAll("\\d+", "***");
// result = "hello ***"

9. 性能优化与最佳实践

9.1 警惕回溯陷阱

正则表达式引擎在处理某些模式时可能会发生灾难性回溯(Catastrophic Backtracking),导致执行时间指数级增长。典型的危险模式包括:

(a+)+b         嵌套量词
(a|aa)+b       重叠的选择分支
(.*a){n}       带有回溯的量词组合

避免方法:

  • 避免嵌套量词(如 (a+)+
  • 使用原子组或占有量词(如果引擎支持)
  • 尽量使表达式具有确定性

9.2 优先使用更具体的字符类

# 不推荐
.+@.+\..+

# 推荐
[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}

# 不推荐
.*foo.*

# 推荐
[^\r\n]*foo[^\r\n]*    # 仅用于单行文本匹配

说明:当你已经知道允许出现的字符范围时,尽量用明确的字符类替代 . 或过于宽泛的 .*。这样通常更容易阅读,也能减少不必要的回溯。

9.3 合理使用锚点

使用 ^$ 可以让引擎更快地排除不匹配的情况,显著提升性能。

9.4 编译并复用正则

在需要多次使用同一正则的场景中,应提前编译并复用:

# Python:编译后复用
pattern = re.compile(r'\d{4}-\d{2}-\d{2}')
for line in lines:
    if pattern.search(line):
        # 处理匹配
        pass
// JavaScript:在循环外创建正则
const pattern = /\d{4}-\d{2}-\d{2}/g;

9.5 不要用正则做所有事情

正则表达式并非万能。以下场景建议使用专用工具:

  • 解析 HTML/XML:使用 DOM 解析器(如 Python 的 BeautifulSoup、JavaScript 的 DOMParser)
  • 解析 JSON:使用 JSON.parse() 等原生方法
  • 复杂的语法分析:使用解析器生成器(如 ANTLR、PEG.js)

10. 在线测试工具

在编写和调试正则表达式时,可视化工具能极大提高效率。我们推荐使用在线 正则表达式测试工具,它具备以下功能:

  • 实时匹配并高亮显示结果
  • 支持多种修饰符(全局、忽略大小写、多行等)
  • 分组捕获结果的可视化

11. 结语

正则表达式是程序员工具箱里最强大的文本处理工具之一。虽然它的语法紧凑且初学时可能令人困惑,但一旦掌握了核心概念——字符类、量词、分组和断言——你就拥有了处理几乎任何文本模式的能力。

学习的最佳方式是实践。赶快打开我们的 在线正则表达式测试工具,动手尝试本文中的每一个示例吧!