在正则表达式中,分组(Grouping)允许我们将多个字符视为一个整体单元,就像数学中的括号一样。分组主要有两个作用:
*、+、?、{n})使用圆括号 () 来创建分组:
(表达式)
例如,(ab)+ 可以匹配 "ab"、"abab"、"ababab" 等,但不能匹配 "a" 或 "b"。
正则表达式中有几种不同类型的分组:
最常见的分组形式,会捕获匹配的内容并分配一个编号(从1开始)。
(\d{4})-(\d{2})-(\d{2}) # 匹配日期格式 YYYY-MM-DD
这个表达式会创建3个分组:
使用 (?:表达式) 语法,表示只分组但不捕获。
(?:Mr|Ms|Mrs)\. (\w+) # 匹配 "Mr. Smith" 但只捕获 "Smith"
为分组指定名称,提高可读性(不同语言语法可能不同)。
Python 示例:
(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})
JavaScript 示例:
(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
分组最强大的功能之一是可以在正则表达式内部或外部引用已匹配的内容。
在正则表达式内部引用前面的分组,使用 \数字 语法:
(\w+) \1 # 匹配重复的单词,如 "hello hello"
这个模式会匹配两个相同的单词,中间用空格分隔。
对于命名分组,可以使用名称来引用:
(?P<word>\w+) (?P=word) # Python 语法
\k<word> # JavaScript 语法
在替换操作中引用分组内容:
Python 示例:
import re
text = "2023-05-15"
new_text = re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\2/\3/\1', text)
# 结果: "05/15/2023"
JavaScript 示例:
let text = "2023-05-15";
let newText = text.replace(/(\d{4})-(\d{2})-(\d{2})/, '$2/$3/$1');
// 结果: "05/15/2023"
<([a-z][a-z0-9]*)\b[^>]*>.*?</\1>
这个模式可以匹配成对的HTML标签(如 <div>...</div>),其中:
([a-z][a-z0-9]*) 捕获标签名\1 引用前面捕获的标签名确保前后一致\b(\w+)\b\s+\1\b
可以找出文本中连续重复的单词。
Python 代码:
import re
date = "2023-12-25"
# 将 YYYY-MM-DD 转换为 DD/MM/YYYY
new_date = re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3/\2/\1', date)
print(new_date) # 输出: 25/12/2023
有些正则引擎支持基于分组的条件匹配:
(?(1)true-pattern|false-pattern)
表示如果分组1已匹配,则匹配 true-pattern,否则匹配 false-pattern。
用于匹配嵌套结构(如括号),需要特定正则引擎支持。
(a)|(b)(如果不需要捕获,应使用 (?:a|b))贪婪匹配问题:
<(.*)> # 会贪婪匹配到最后一个 >
应使用:
<(.*?)> # 非贪婪匹配
user@domain.com;user@domain.com)(123) 456-7890 到 123-456-7890<img src="..." alt="..."> 中的 src 和 alt)| 概念 | 语法 | 用途 |
|---|---|---|
| 捕获分组 | (pattern) | 捕获匹配内容并分配编号 |
| 非捕获分组 | (?:pattern) | 分组但不捕获 |
| 命名分组 | (?P<name>pattern) (Python) | 为分组指定名称 |
| 反向引用 | \1, \2 等 | 引用前面匹配的分组 |
| 命名反向引用 | (?P=name) (Python) | 按名称引用分组 |
| 替换引用 | $1, $2 或 \1, \2 | 在替换字符串中引用分组 |
掌握正则表达式的分组和引用功能,可以让你: