正则表达式,或称为 RegEx(Regular Expression),是一系列字符,形成一个搜索模式。

正则表达式可以用来检查字符串是否包含指定的搜索模式。

常用符号

字符匹配

普通字符:字母、数字和普通符号(如 a、1、!)在正则表达式中都代表它们本身。

转义字符:”\“ 取消元字符的特殊意义,或表示一个预定义字符类。

. :匹配任意单个字符(除换行符)。


字符类

1
2
3
4
5
[abc]:匹配 a、b 或 c 中的任意一个字符。
[a-z]:匹配小写字母 a 到 z 之间的任意字符。
[A-Z]:匹配大写字母 A 到 Z 之间的任意字符。
[0-9]:匹配数字 0 到 9 之间的任意字符。
[^xxx]:表示不匹配字符类中的任意字符。

示例1:[^0-9] 匹配任何非数字字符。示例:[^0-9] 匹配任何非数字字符。

示例2:b[aeiou]t 匹配 “bat”、”bet”、”bit”、”bot”、”but”。


预定义字符类:

1
2
3
4
5
6
\d  匹配任何十进制数;它相当于类 [0-9]。
\D 匹配任何非数字字符;它相当于类 [^0-9]。
\s 匹配任何空白字符(空格、制表符等);它相当于类 [ \t\n\r\f\v]。
\S 匹配任何非空白字符;它相当于类 [^ \t\n\r\f\v]。
\w 匹配任何字母数字字符或下划线;它相当于类 [a-zA-Z0-9_]。
\W 匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]。

量词

1
2
3
4
5
6
*:匹配前一个字符 0 次或多次。
+:匹配前一个字符 1 次或多次。
?:匹配前一个字符 0 次或 1 次。
{n}:匹配前一个字符恰好 n 次。
{n,}:匹配前一个字符至少 n 次。
{m,n}:匹配前一个字符 m 到 n 次。

注:”m,n”之间不能有空格,n的缺省值是无穷


边界

边界用于指定匹配的位置

1
2
3
4
^:匹配字符串的开始。
$:匹配字符串的结束。
\b:匹配单词边界。
\B:匹配非单词边界。

示例1:^hello 只能匹配以 “hello” 开头的字符串

示例2:world$ 只能匹配以 “world” 结尾的字符串。

示例3:\bword\b 只匹配独立的 “word” 而不是 “sword”。

示例4:\Bword\B 匹配 “sword” 中的 “word”。


组和引用

捕获组:用 (…) 将部分表达式括起来,用于将匹配的内容提取出来。
示例1:(abc) 会捕获字符串中的 “abc”。

引用捕获组:在正则表达式中,可以使用 \1、\2 等来引用之前的捕获组。
示例2:(\d)\1 匹配重复的数字,如 “11”、”22”。


管道符

|:表示“或”操作。”|”的优先级非常低,是为了当你有多字符串要选择时能适当地运行。


示例

匹配 URL

1
https?://([\w.-]+\.)+[a-zA-Z]{2,6}(/[\w&%?./-]*)?

匹配邮箱地址

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

匹配 IPv4 地址

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

匹配电话号码(+86)

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

匹配中国大陆居民身份证ID

1
^\d{6}(18|19|20)?\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}(\d|X|x)?$

提取页面中的图片链接

1
<img\s+[^>]*src="([^"]+)"

提取json数据

1
<script[^>]*>.*?({.*?}).*?</script>

进阶(待续)

零宽断言

零宽断言(Zero-width assertions)是一种特殊正则构造,在不消耗字符的情况下进行条件匹配。

零宽断言主要有四种类型:

Positive Lookahead

用于匹配条件满足后的位置

基本语法:

1
(?=xxx)

示例:

1
\w+(?=\d)   # 匹配后面跟着一个数字的一个或多个字母数字

Negative Lookahead

用于匹配条件不满足后的位置

基本语法:

1
(?!xxx)

示例:

1
\w+(?!\d)   # 匹配后面不能跟着一个数字的一个或多个字母数字

Positive Lookbehind

匹配条件前面的位置

基本语法:

1
(?<=xxx)

示例:

1
(?<=@)\w+   # 匹配前面必须带@的一个或多个字母数字

Negative Lookbehind

匹配条件不在前面的位置

基本语法:

1
(?<!xxx)

示例:

1
(?<!@)\w+   # 匹配前面不能有@的一个或多个字母数字

命名捕获组

命名捕获组是正则表达式中的一种增强特性,用于将匹配的内容以命名进行捕获。和传统捕获组的不同在于:命名捕获组通过给组指定一个名称,使提取数据时更加直观。

基本语法:

1
(?<name>...)

示例:解析URL(Python)

1
2
3
4
5
6
7
8
9
10
11
import re

url = "https://www.example.com/path/to/resource"
pattern = r'^(?P<protocol>https?)://(?P<hostname>[^/]+)(?P<path>/.*)?$'

match = re.match(pattern, url)

if match:
print(f"Protocol: {match.group('protocol')}")
print(f"Hostname: {match.group('hostname')}")
print(f"Path: {match.group('path')}")

条件匹配

条件匹配在正则中用于根据特定条件选择性地应用不同的匹配模式。条件匹配通常结合捕获组和特定语法来实现。

基本语法:

1
2
3
4
(?(n)yes|no)  
# n是捕获组的编号
# yes 是当捕获组 n 匹配时执行的模式。
# no 是当捕获组 n 不匹配时执行的模式。

示例1:匹配可选前缀 pre-

1
(?(1)pre-|\b)([a-zA-Z]+)

条件部分 (?(1)pre-|\b):

?(1):表示一个条件语句,检查第一个捕获组 1 是否匹配。
pre-:如果捕获组 1 匹配,则要求匹配前缀 pre-。
|:分隔符,表示条件的两种可能情况。
\b:如果捕获组 1 不匹配,则匹配一个单词边界。这里的 \b 确保了如果没有前缀 pre-,则后面的匹配部分可以单独存在,不与前面的字母相连。

主匹配部分 ([a-zA-Z]+)


示例2:日期格式

1
(?:(?P<d>\d{1,2})/(?P<m>\d{1,2})/(?P<y>\d{4})|(?P<y2>\d{4})-(?P<m2>\d{1,2})-(?P<d2>\d{1,2}))

d/m/y 格式
y-m-d 格式


回溯引用

回溯引用(Backreference)是一种在正则表达式中使用的特性,它允许你在模式中引用之前匹配的捕获组。

捕获组可以通过 \n 引用自己之前匹配的内容,n 为捕获组的编号或名称。

示例1:匹配重复出现的单词

1
\b(\w+)\s+\1\b

\b:表示单词边界。
(\w+):匹配一个或多个字母或数字,并将其捕获为组1。
\s+:匹配一个或多个空白字符。
\1:引用捕获组1,表示与之前匹配的单词相同。

示例2:匹配括号成对

1
\((.*?)\)\(\1\)

\( 和 \):匹配左括号和右括号。
(.*?):匹配任意字符(非贪婪模式),并将其捕获为组1。
\1:引用组1,确保两个括号中的内容相同。


嵌套组和递归

递归是指在正则表达式中调用自身的能力,允许正则表达式通过递归调用来匹配嵌套结构。

示例1:匹配括号成对

1
\(([^()]|(?R))*\)

\( 和 \):匹配左括号和右括号。
[^()]*:匹配任意不包含括号的字符。
(?R):表示递归匹配整个正则表达式,使得可以匹配嵌套的括号。

示例2: 匹配标签嵌套

1
<([a-zA-Z][a-zA-Z0-9]*)>(?:[^<]+|(?R))*<\/\1>

<([a-zA-Z][a-zA-Z0-9]*)>:匹配开始标签,并捕获标签名。
(?:[^<]+|(?R))*:匹配非标签内容或递归调用自身以匹配嵌套标签。
<\/\1>:匹配结束标签,\1 用于引用开始标签的名字。


模式修饰符

模式修饰符用于改变正则表达式的行为,通常在模式的开头添加。

常见的修饰符包括:

1
2
3
4
i:忽略大小写。
m:多行模式,使 ^ 和 $ 能匹配每一行的开头和结尾。
s:单行模式,使 . 能匹配包括换行符在内的所有字符。
x:忽略表达式中的空白符和注释,使正则表达式更易读。

示例:忽略大小写匹配“hello”:

1
(?i)hello

贪婪匹配与懒惰匹配

贪婪匹配和懒惰匹配是正则表达式中控制重复匹配行为的两种策略。

贪婪匹配

贪婪匹配是尽可能多地匹配字符

示例:匹配引号之间的所有内容

1
".*"

懒惰匹配

贪婪匹配是通过在量词后添加 ? 来实现懒惰匹配,即尽可能少地匹配字符。

示例:匹配引号之间的最短内容

1
".*?"

原子组

原子组通过使用 (?>xxx) 来定义,它将一个子模式标记为“原子”,表示该组的内容在匹配后不能被重新评估或回溯。

基本语法:

1
(?>xxx)

示例:

1
(?>\d+)\d

\d+:表示匹配一个或多个数字字符(0-9)。
\d:表示匹配一个数字字符。


模式注释

通过 (?x) 开启模式注释,可以在正则表达式中加入空格和注释,提升可读性。

此时,正则表达式中的空白符(空格、换行符等)会被忽略,除非它们在字符类 […] 内被转义。

示例1:匹配邮箱地址格式(通用)

1
2
3
4
5
(?x)                   # 启用模式注释
[a-zA-Z0-9._%+-]+ # 用户名部分,可以包含字母、数字和一些特殊字符
@ # at 符号
[a-zA-Z0-9.-]+ # 域名,可以包含字母、数字和点号
\.[a-zA-Z]{2,} # 顶级域名,至少两位字母

示例2:匹配日期(Python:在 re.compile() 中使用 re.VERBOSE)

1
2
3
4
5
6
7
8
import re
pattern = re.compile(r"""
\d{4} # 年份,四位数字
- # 中间的连字符
\d{2} # 月份,两位数字
- # 中间的连字符
\d{2} # 日期,两位数字
""", re.VERBOSE)

参考

在线工具