正则表达式完整语法参考

基于 JS/PCRE 风格,适用于 VS Code 搜索(ripgrep 引擎)。

1. 字符匹配

语法 含义
. 任意单个字符(默认不含换行)
\d 数字 [0-9]
\D 非数字 [^0-9]
\w 单词字符 [a-zA-Z0-9_]
\W 非单词字符
\s 空白字符(空格、tab、换行等)
\S 非空白字符
\t 制表符
\n 换行符
\r 回车符
\\ 转义反斜杠本身
\. \( \[ 转义特殊字符,匹配字面量

2. 字符集

语法 含义
[abc] 匹配 a、b 或 c
[^abc] 不匹配 a、b、c
[a-z] a 到 z 范围
[a-zA-Z0-9] 字母和数字
[^a-z] 非小写字母

3. 量词

语法 含义
* 0 次或多次(贪婪)
+ 1 次或多次(贪婪)
? 0 次或 1 次
{n} 恰好 n 次
{n,} 至少 n 次
{n,m} n 到 m 次
*? 0 次或多次(懒惰/非贪婪)
+? 1 次或多次(懒惰)
{n,m}? n 到 m 次(懒惰)

贪婪 vs 懒惰:

# 贪婪:匹配尽可能多
".*"    对 "a" and "b" → 匹配整个 "a" and "b"

# 懒惰:匹配尽可能少
".*?"   对 "a" and "b" → 分别匹配 "a" 和 "b"

4. 锚点/位置

语法 含义
^ 行首
$ 行尾
\b 单词边界
\B 非单词边界

5. 分组与捕获

语法 含义
(abc) 捕获组,替换时用 $1 引用
(?:abc) 非捕获组(仅分组,不捕获,不占编号)
(?<name>abc) 命名捕获组,替换时用 $<name> 引用
\1 \2 反向引用第 1、2 个捕获组
\k<name> 反向引用命名捕获组
(a\|b) 或匹配

5.1 非捕获组 (?:...)

分组但不捕获,不占用 $1 编号。常用于只需要分组逻辑但不需要引用的场景。

# 捕获组:
(https?)://(\w+)
# $1 = https, $2 = example

# 非捕获组:
(?:https?)://(\w+)
# $1 直接就是 example,协议部分不占编号

5.2 反向引用 \1 \2

在同一个正则表达式内部引用前面捕获组匹配到的实际内容。用于匹配"重复出现的相同文本"。

# 查找连续重复单词(比如 "the the")
\b(\w+)\s+\1\b
# \1 引用第一个捕获组匹配到的实际内容
# "the the" → 匹配 ?
# "the that" → 不匹配 ?

# 查找 HTML 中开闭标签匹配的元素
<(\w+)>.*?</\1>
# <div>hello</div>  → 匹配 ?
# <div>hello</span> → 不匹配 ?

注意区分:\1 用在查找表达式中(反向引用),$1 用在替换表达式中(捕获引用)。

5.3 捕获组编号:可以超过 9

不限于 1-9,$10\10 都可以使用。

# 假设有 10 个捕获组
(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)

\10  → 引用第 10 个组(j)
$10  → 替换时引用第 10 个组

歧义规则:如果存在第 10 个捕获组,\10 引用第 10 个;否则解释为 \1 + 字面量 "0"。为避免歧义,推荐使用命名捕获组。

5.4 命名捕获组 (?<name>...)

# 交换姓名顺序
查找:(?<last>\w+),\s*(?<first>\w+)
替换:$<first> $<last>
# "Zhang, San" → "San Zhang"

命名捕获组也支持反向引用,用 \k<name>

# 查找重复单词(命名版本)
\b(?<word>\w+)\s+\k<word>\b
# "hello hello" → 匹配 ?
# 等价于 \b(\w+)\s+\1\b,但可读性更好

命名组同时也有编号,$1$<name> 都能用:

(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
# $<year> 等于 $1
# $<month> 等于 $2
# $<day> 等于 $3

6. 零宽断言(前瞻/后顾)

零宽断言只做条件判断,不消耗字符,匹配结果中不包含断言部分的文本。

语法 名称 含义
(?=...) 正向前瞻 (Positive Lookahead) 后面必须跟着 ...
(?!...) 负向前瞻 (Negative Lookahead) 后面不能跟着 ...
(?<=...) 正向后顾 (Positive Lookbehind) 前面必须是 ...
(?<!...) 负向后顾 (Negative Lookbehind) 前面不能是 ...
# 正向前瞻:匹配后面跟着 "(" 的单词(找函数名)
\w+(?=\()
# "foo()" 中匹配 "foo","bar = 1" 中不匹配

# 负向前瞻:找不跟着数字的 foo
foo(?!\d)
# "fooBar" 匹配,"foo123" 不匹配

# 正向后顾:匹配前面是 "SL" 的类名部分
(?<=SL)\w+
# "SLGameView" 中匹配 "GameView"

# 负向后顾:匹配前面不是字母的数字
(?<![a-zA-Z])\d+
# "x100" 中不匹配,"= 100" 中匹配 "100"

注意:VS Code(ripgrep)中后顾要求模式是固定长度的,不能在 (?<=...) 里用 *+ 等不定长量词。

7. 标志/修饰符

VS Code 搜索栏中通过按钮切换。在代码中写正则时,标志加在末尾(如 /pattern/flags)。

标志 含义
i 大小写不敏感
g 全局匹配(找所有,不只第一个)
m 多行模式,^$ 匹配每行的开头和结尾
s 单行/dotAll 模式,. 也匹配换行符

示例(JavaScript 代码中的用法)

// i - 大小写不敏感
/hello/i
// 匹配 "Hello"、"HELLO"、"hElLo"

// g - 全局匹配
"aaa".match(/a/g)    // ["a", "a", "a"]
"aaa".match(/a/)     // ["a"](只找第一个)

// m - 多行模式
`line1
line2
line3`.match(/^\w+/gm)   // ["line1", "line2", "line3"]

`line1
line2
line3`.match(/^\w+/g)    // ["line1"](只匹配第一行开头)

// s - 单行/dotAll 模式
"a\nb".match(/a.b/)      // null(. 不匹配 \n)
"a\nb".match(/a.b/s)     // ["a\nb"] ?

VS Code 搜索栏对应

注意:以上标志示例是 JavaScript 代码中的写法,VS Code 搜索栏本身不支持写代码逻辑,只支持纯正则表达式和替换模板。

8. 特殊字符(需转义才能匹配字面量)

. * + ? ^ $ { } [ ] ( ) | \ /

\ 转义,比如匹配 C++ 要写 C\+\+

9. VS Code 替换中的特殊语法

语法 含义
$0 引用整个匹配
$1 $2 引用捕获组
$<name> 引用命名捕获组
\n 替换中插入换行
\u$1 首字母大写
\l$1 首字母小写
\U$1 全部大写
\L$1 全部小写

大小写转换示例:

# snake_case → camelCase
查找:(\w+)_(\w+)
替换:$1\u$2

# 全部转大写
查找:(\w+)
替换:\U$1

9.1 大括号语法 ${n:/transform}(仅限 Snippet)

⚠️ 大括号转换语法是 VS Code Snippet(代码片段)专用语法,在搜索替换(Ctrl+H)中不生效,会被当作字面量输出。搜索替换中请使用 \u \U \l \L

大括号语法来自 TextMate/snippet 风格,用于 snippet 定义文件(.code-snippets)或 snippet 相关的场景中。

大括号内可以用编号或命名捕获组名:

语法 含义 适用场景
${1:/upcase} 将捕获组 1 全部转大写 Snippet
${1:/downcase} 将捕获组 1 全部转小写 Snippet
${1:/capitalize} 将捕获组 1 首字母大写 Snippet
${1:/pascalcase} 将捕获组 1 转为 PascalCase Snippet
${1:/camelcase} 将捕获组 1 转为 camelCase Snippet
${name:/upcase} 将命名捕获组全部转大写 Snippet
${name:/downcase} 将命名捕获组全部转小写 Snippet
${name:/capitalize} 将命名捕获组首字母大写 Snippet
${name:/pascalcase} 将命名捕获组转为 PascalCase Snippet
${name:/camelcase} 将命名捕获组转为 camelCase Snippet

与搜索替换中简写语法的对应关系:

Snippet 大括号语法 搜索替换等价写法
${1:/upcase} \U$1
${1:/downcase} \L$1
${1:/capitalize} \u$1
${1:/pascalcase} 无等价写法 ❌
${1:/camelcase} 无等价写法 ❌

9.2 搜索替换 vs Snippet:语法对比

功能 搜索替换(Ctrl+H) Snippet(.code-snippets)
引用捕获组 $1$<name> $1${1}${name}
全部大写 \U$1 ${1:/upcase}
全部小写 \L$1 ${1:/downcase}
首字母大写 \u$1 ${1:/capitalize}
首字母小写 \l$1
PascalCase 不支持 ❌ ${1:/pascalcase}
camelCase 不支持 ❌ ${1:/camelcase}

简单记:搜索替换用反斜杠(\U \u \L \l),Snippet 用大括号(${n:/transform})。两套语法不能混用。

9.3 PascalCase 与 camelCase 说明

这两个是编程中常见的命名风格:

风格 规则 示例
PascalCase 每个单词首字母大写 MyVariableGameViewHttpRequest
camelCase 第一个单词小写,后续单词首字母大写 myVariablegameViewhttpRequest

在 Snippet 大括号语法中,/pascalcase/camelcase 会把捕获内容按分隔符(下划线、连字符、空格等)拆分成单词,然后按对应风格拼接:

输入 "hello_world"
  /pascalcase → "HelloWorld"
  /camelcase  → "helloWorld"

输入 "my-component"
  /pascalcase → "MyComponent"
  /camelcase  → "myComponent"

输入 "SOME_CONST"
  /pascalcase → "SomeConst"
  /camelcase  → "someConst"

9.4 Snippet 中的大括号语法示例

以下示例用于 .code-snippets 文件中的 snippet transform:

// snippet 中对变量做正则变换的写法:
// ${变量/正则/替换/标志}

// 文件名转 PascalCase 作为类名
"body": ["class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/} {", "}"]
// 文件名 "my-component" → class MyComponent { }

// 文件名转 camelCase 作为变量名
"body": ["const ${TM_FILENAME_BASE/(.*)/${1:/camelcase}/} = {}"]
// 文件名 "my-component" → const myComponent = {}

// 命名捕获组 + 大括号语法
"body": ["${TM_FILENAME_BASE/(?<name>.*)/${name:/upcase}/}"]
// 文件名 "utils" → UTILS

9.5 搜索替换中的大小写转换示例

以下示例用于 VS Code 搜索替换(Ctrl+H):

# snake_case → camelCase(手动方式,因为搜索替换不支持 /camelcase)
查找:(\w+)_(\w)(\w*)
替换:$1\u$2$3
# "my_var" → "myVar"
# 注意:只处理一个下划线,多段需要多次替换

# 全部转大写
查找:(\w+)
替换:\U$1

# 首字母大写
查找:\b(\w)(\w*)
替换:\u$1$2

# 命名捕获组 + 大小写转换
查找:(?<first>\w+),\s*(?<last>\w+)
替换:\U$<first> \L$<last>
# "hello, WORLD" → "HELLO world"

10. 实用示例汇总

# 查找所有函数声明
\b\w+\s+\w+\(.*\)

# 查找 TODO 或 FIXME 注释
(TODO|FIXME).*

# 查找特定类的方法调用
SLGameView\.\w+

# 查找 #include 引用
#include\s+["<].*[">]

# 替换:保留参数,改函数名
查找:oldFunc\(([^)]*)\)
替换:newFunc($1)

# 替换浮点数后缀 f → F
查找:(\d+\.\d+)f
替换:$1F

# 查找连续重复单词
\b(\w+)\s+\1\b

# 交换逗号分隔的两部分
查找:(?<a>\w+),\s*(?<b>\w+)
替换:$<b>, $<a>