std::regex ECMAScript 语法完整参考
C++ 中
std::regex默认使用的正则语法(std::regex_constants::ECMAScript)。
该语法基于ECMAScript 3(ECMA-262 第 3 版),并在此基础上做了少量修改与限制。
目录
- 一、语法选择与标志
- 二、核心文法解析
- 三、元素详解
- 1. 字符类 Character Classes
- 2. 量词 Quantifiers
- 3. 断言 Assertions
- 4. 分组与反向引用
- 5. 转义序列
- 四、括号表达式细节
- 五、匹配语义
- 六、替换格式
- 七、C++ 特有的修改与限制
- 八、一页速查表
- 九、参考资料
一、语法选择与标志
#include<regex>usingnamespacestd::regex_constants;可选语法(syntax_option_type)
| 取值 | 说明 |
|---|---|
ECMAScript | 默认。最接近 JavaScript / .NET 语言的语法 |
basic | POSIX 基本正则(BRE) |
extended | POSIX 扩展正则(ERE) |
awk | extended + 更多非打印字符转义 |
grep | basic + 允许\n分隔分支 |
egrep | extended + 允许\n分隔分支 |
一次只能指定一种语法。
可叠加的标志
| 标志 | 作用 |
|---|---|
icase | 忽略大小写 |
nosubs | 忽略标记匹配(括号内的表达式),不保存替换内容 |
optimize | 让匹配更快,代价是构造时间可能更长 |
collate | 使用区域敏感的排序序列(如[a-z]按 locale 解释) |
可以叠加零个或多个标志。若只指定标志而不指定语法,默认
ECMAScript。
std::regexre1(R"(\d+)",std::regex::ECMAScript);std::regexre2(R"(abc)",std::regex::icase|std::regex::ECMAScript);std::regexre3(R"(abc)");// 默认即 ECMAScript二、核心文法解析
一个正则表达式文法由多个可选项(Alternative)组成,各项之间用析取运算符|分隔。
匹配优先级
|的优先级最低。匹配时引擎会优先尝试匹配左侧的可选项;只有左侧失败,才会尝试右侧。
b|bc// 在 "abcd" 中 search// → 匹配 "b"(左项先成功,不再尝试右项 "bc")这就是 ECMAScript 的**「首次匹配」**语义,与 POSIX 的「最长匹配」不同。详见 五、匹配语义。
捕获行为
若左侧可选项匹配成功,则右侧可选项中定义的捕获组会产生「空子匹配」(empty submatches)。
(a)|(b)// 匹配 "a" 时:// 捕获组 1 = "a"// 捕获组 2 = 空子匹配(matched = false)C++ 中通过std::smatch检查:
std::string s="a";std::smatch m;std::regexre(R"((a)|(b))");if(std::regex_search(s,m,re)){m[1].matched;// true, m[1] = "a"m[2].matched;// false ← 空子匹配m[2].str();// ""(但 matched 为 false)}三、元素详解
1. 字符类 Character Classes
用于匹配一个字符集合中的任意一个字符。
| 语法 | 含义 |
|---|---|
[abc] | 匹配a、b、c中的任意一个字符 |
[^abc] | 否定字符类,匹配除a、b、c之外任意字符 |
[a-z] | 匹配a到z范围内的任意小写字母 |
\d、\D | 匹配数字 / 非数字(C++ 中为区域敏感) |
\s、\S | 匹配空白字符 / 非空白字符(C++ 中为区域敏感) |
\w、\W | 匹配单词字符 / 非单词字符(C++ 中为区域敏感) |
[[:alpha:]] | POSIX 字符类,匹配字母 |
[=elt=] | 等价类,匹配与elt排序等价的字符 |
dsw 快捷转义与具名类的对应关系:
| 转义 | 等价具名类 | 默认类 |
|---|---|---|
\d | [[:d:]] | [[:digit:]] |
\D | [^[:d:]] | [^[:digit:]] |
\s | [[:s:]] | [[:space:]] |
\S | [^[:s:]] | [^[:space:]] |
\w | [[:w:]] | [a-zA-Z0-9_](ASCII) |
\W | [^[:w:]] | [^a-zA-Z0-9_](ASCII) |
POSIX 具名类([:name:])默认支持:
| 名称 | 含义 |
|---|---|
alnum | 大小写字母 + 数字 |
alpha | 大小写字母 |
blank | 空格或制表符 |
cntrl | 控制字符 |
digit | 数字 |
graph | 字母 + 数字 + 标点 |
lower/upper | 小写 / 大写字母 |
print | 字母 + 数字 + 标点 + 空格 |
punct | 标点 |
space | 空格 |
xdigit | 十六进制数字 |
d/s/w | 分别同digit/space/alnum |
⚠️
\w默认只认 ASCII 字符,匹配中文等 Unicode 字母需要自己写字符类。
通配符.
.// 匹配目标序列中除换行(\n)以外的任意字符普通字符(需转义的特殊字符)
ECMAScript 中有特殊含义的字符固定为这 13 个:
^ $ \ . * + ? ( ) [ ] { } |标识转义(Identity Escape)
反斜杠 + 单个字符,匹配该字符本身,用于去掉特殊含义。
a*// 匹配 "aaa"a\*// 匹配字面量 "a*",不匹配 "aaa"各语法允许标识转义的字符集不同:
| 语法 | 允许标识转义的字符 |
|---|---|
basic/grep | ( ) { } . [ \ * ^ $ |
extended/egrep | ( ) { . [ \ * ^ $ + ? | |
awk | 上述 +"/ |
| ECMAScript | 除「可构成标识符的字符」外全部—— 字母、数字、$、_、Unicode 转义序列不能被标识转义 |
2. 量词 Quantifiers
用于指定前一个元素重复的次数。
| 语法 | 含义 |
|---|---|
* | 匹配零次或多次(等价{0,}) |
+ | 匹配一次或多次(等价{1,}) |
? | 匹配零次或一次(等价{0,1}) |
{n} | 恰好匹配 n 次 |
{n,} | 至少匹配 n 次 |
{n,m} | 匹配 n 到 m 次 |
贪婪与非贪婪
默认情况下量词是贪婪的 —— 会尽可能多地匹配字符。
(a+)(a*b)// 目标 "aaab"// 贪婪: 组1="aaa", 组2="b"在量词后加?可变为非贪婪模式:
(a+?)(a*b)// 目标 "aaab"// 非贪婪:组1="a", 组2="aab"可用于量词的元素:除(?=、(?!、^、$之外的所有元素。
以下示例均按整串匹配(
std::regex_match)语义理解。
a{2,3}// 匹配 "aa" / "aaa",不匹配 "a" / "aaaa"a{2}// 只匹配 "aa"a{2,}// 匹配 "aa" 及以上ab+// 整串匹配 "abb",不匹配 "abab"(ab)+// 不匹配 "abb",匹配 "abab"⚠️注意区分
regex_match与regex_search。regex_search只要求存在子序列匹配即可,因此用regex_search时ab+能在"abab"中找到"ab",(ab)+能在"abb"中找到"ab"。
详见 五、匹配语义。
3. 断言 Assertions
断言不消耗字符,只用于检查某个条件是否成立(零宽匹配)。
| 语法 | 含义 |
|---|---|
^ | 匹配输入的开头 |
$ | 匹配输入的结尾 |
\b | 匹配单词边界 |
(?=...) | 正向先行断言:要求右侧能匹配... |
(?!...) | 负向先行断言:要求右侧不能匹配... |
(?<=...) | 正向后行断言:要求左侧能匹配... |
(?<!...) | 负向后行断言:要求左侧不能匹配... |
⚠️后行断言(
(?<=)/(?<!))在 C++ 标准中不属于 ECMAScript 文法,std::regex不支持。
上表列出是为了与 JavaScript 对照 —— 现代 JS 已支持后行断言,C++ 没有跟进。
单词边界出现的四种情况:
- 当前字符在序列开头,且是单词字符(
A-Za-z0-9_) - 当前位置越过序列结尾,且最后一个字符是单词字符
- 当前字符是单词字符,前一个不是
- 当前字符不是单词字符,前一个是
示例:
(?=a)a// 匹配 "a"(?!a)a// 不匹配 "a"a\b.// 匹配 "a~",不匹配 "ab"a\B.// 匹配 "ab",不匹配 "a~"(?!aa)(a*)// 匹配 "a"(组1="a"),不匹配 "aa" / "aaa"(?=aa)(a*)// 匹配 "aaaa",组1="aaaa"⚠️
\b在 ECMAScript 下是单词边界断言,不是退格符。
退格符的写法见 5. 转义序列。
4. 分组与反向引用
| 语法 | 含义 |
|---|---|
(...) | 捕获组,将子模式分组并捕获匹配的文本 |
(?:...) | 非捕获组,只分组,不捕获文本 |
\n | 反向引用,引用第 n 个捕获组匹配到的文本 |
捕获组编号:由左括号的出现顺序决定(数到该左括号为止的开括号个数)。
((a+)(b+))(c+)// 组1 = "aabbb"// 组2 = "aa"// 组3 = "bbb"// 组4 = "c"反向引用的编号解析差异(重要):
| 语法 | N 的确定方式 | 上限 |
|---|---|---|
basic/grep | 只取反斜杠后的一位十进制数字 | N ≤ 9 |
| ECMAScript | 取反斜杠后连续的所有十进制数字 | 无上限 |
((a+)(b+))(c+)\3// 匹配 "aabbbcbbb"// \3 引用组3 的内容 "(b+)"(a)\2// 非法(不存在组2)(b(((((((((a))))))))))\10// ECMAScript:\10 = 第 10 个捕获组(最内层那个)// basic: \1 = 第 1 个捕获组,后面的 0 是普通字符 '0'非捕获组与反向引用:
(?:a)// 合法,但不会产生捕获组(?:a)\1// 非法 —— 不存在第 1 个捕获组⚠️ C++ 实现限制:捕获组最多 31 个。
分组对量词的影响:
以下按整串匹配理解。
ab+// 整串匹配 "abb"(+ 只作用于 b)(ab)+// 整串匹配 "abab"(+ 作用于整个 "ab")5. 转义序列
| 类型 | 形式 | 含义 |
|---|---|---|
| 标识转义 | \k | 匹配字符k本身 |
| 文件格式转义 | \f\n\r\t\v | 换页 / 换行 / 回车 / 水平制表 / 垂直制表 |
| 十六进制转义 | \xhh | 两位十六进制数表示一个字符 |
| Unicode 转义 | \uhhhh | 四位十六进制数表示一个字符 |
| 控制字符转义 | \ck | 匹配k命名的控制字符(k为a-z/A-Z) |
⚠️ ECMAScript 下\a与\b不作文件格式转义:
\b是单词边界断言\a不允许(basic/awk里才表示响铃)
示例:
"\ci"// 匹配 "\x09"(Ctrl+I = 0x09)"\x41"// ASCII 下匹配 "a""\u0041"// ASCII 下匹配 "a"\.\.\*\+\?\(\)\[\]\{\}\|\^\$ \\C++ 中的字符串字面量陷阱:
正则里的\在 C++ 字符串字面量中本身需要转义,否则编译器会先吃掉一层。建议一律用原始字符串字面量(Raw String Literal):
std::regexbad(R"(\d+)");// ✅ 正确:正则收到 \d+std::regexwrong("\\d+");// ✅ 正确但易错std::regexoops("\d+");// ❌ 危险:\d 不是合法转义序列,行为未定义四、括号表达式细节
[expr]内可包含以下构成的任意组合:
| 构成 | 形式 | 说明 |
|---|---|---|
| 单个字符 | abc | 将该字符加入集合 |
| 字符范围 | ch1-ch2 | 将闭区间[ch1, ch2]内的字符加入集合 |
| 字符类 | [:name:] | 将具名类中的字符加入集合 |
| 等价类 | [=elt=] | 将与elt等价的排序元素加入集合 |
| 排序符号 | [.elt.] | 将排序元素elt加入集合 |
]的处理 —— ECMAScript 与其他语法的分水岭
// 其他语法:位于开头的 ] 表示自身[]abc]// 匹配 a b c ][^]abc]// 匹配除 a b c ] 外的字符// ECMAScript:必须转义[]a// 匹配 "a"(空括号表达式 + 普通字符 a)[\]abc]// 匹配 a b c ]^的位置
仅当位于括号表达式开头时表示取反,其他位置表示自身。
[abc]// a 或 b 或 c[^abc]// 非 a b c[a^bc]// a、b、c 或 ^-的位置
在开头、结尾、或作为某个范围的首尾字符时,表示自身。
[0-7]// { 0,1,2,3,4,5,6,7 }[-0-24]// { -, 0, 1, 2, 4 }[0-2-]// { 0, 1, 2, - }[+--]// { +, ',', - } (ASCII)范围依赖平台字符编码
[h-k]// ASCII:{ h, i, j, k }// EBCDIC:{ h, i, \x8A, ..., \x90, j, k }(因为 h=0x88, k=0x92)使用
collate标志时,范围内的字符由 locale 的排序规则决定。
五、匹配语义
regex_matchvsregex_search
| 要求 | 示例 | |
|---|---|---|
std::regex_match | 整个正则匹配整个目标序列 | bcd不匹配"abcd",也不匹配"bcde" |
std::regex_search | 目标中存在子序列匹配即可 | bcd能匹配"abcd"、"bcde" |
bcd 在"abcd"中 search → 匹配后三个字符 bcd 在"bcde"中 search → 匹配前三个字符 bcd 在"bcdbcd"中 search → 匹配前三个字符(最左)首次匹配 vs 最长匹配
同一位置存在多个匹配时的选择策略:
| 策略 | 规则 |
|---|---|
| 首次匹配(First Match) | ECMAScript 使用。取正则匹配过程中先找到的那个 |
| 最长匹配(Longest Match) | 取该位置最长的子序列;长度相同时取先找到的 |
b|bc 在"abcd"中 search// ECMAScript(首次匹配)→ "b"// 最长匹配 → "bc"部分匹配 Partial Match
匹配到达目标序列末尾但未到达正则末尾,也算成功。
ab 部分匹配"a"✅ ab 部分匹配"ac"❌- 部分匹配成功后,向目标追加字符可能导致后续部分匹配失败
- 部分匹配失败后,向目标追加字符不可能导致后续部分匹配成功
std::regexre("ab");std::smatch m;std::regex_search(s,m,re,std::regex_constants::match_partial);六、替换格式
std::regex_replace的替换串中可使用以下格式:
| ECMAScript 规则 | sed 规则 | 替换为 |
|---|---|---|
$& | & | 整个匹配[match[0].first, match[0].second) |
$$ | — | 字面量$ |
| — | \& | 字面量& |
$` | — | 匹配之前的文本(前缀) |
$' | — | 匹配之后的文本(后缀) |
$n | \n | 第 n 个捕获组(n = 0–9) |
| — | \\n | 字面量\ |
$nn | — | 第 nn 个捕获组(nn = 10–99) |
std::string s="2026-09-18";std::regexre(R"((\d{4})-(\d{2})-(\d{2}))");// 换成 年/月/日std::string out=std::regex_replace(s,re,"$1/$2/$3");// "2026/09/18"七、C++ 特有的修改与限制
C++ 的 ECMAScript 文法基于 ECMAScript 3,并做了以下修改:
1. POSIX 类型扩展
在字符类(character class)内部引入了对本地环境(locale)的 POSIX 类型扩展。
[[:alpha:]]// 匹配字母(POSIX 具名类)[[:digit:]]// 匹配数字[[:space:]]// 匹配空白2. 区域敏感
\d、\s、\w等字符类在 C++ 中是区域敏感(locale-sensitive)的。
也就是说,它们的行为可能随
std::locale设置而改变,与 JavaScript 的固定 ASCII 行为不同。
3. 不支持的特性
C++ 标准库的std::regex不支持以下语法:
| 不支持的语法 | 说明 | JS 是否支持 |
|---|---|---|
(?<=...) | 后行断言 | ✅ 现代 JS 支持 |
(?<!...) | 否定后行断言 | ✅ 现代 JS 支持 |
(?<name>...) | 具名捕获组 | ✅ |
\k<name> | 具名反向引用 | ✅ |
\p{...} | Unicode 属性类 | ✅ |
\Q...\E | 引用块 | ❌(PCRE 支持) |
x++/x*+ | 占有量词 | ❌(PCRE/Java 支持) |
(?(cond)...) | 条件表达式 | ❌(PCRE 支持) |
4. 语法选项互斥
ECMAScript是默认文法;在basic、extended、awk、grep、egrep等选项中最多只能选择一种。
5. 其他行为差异
| 项目 | C++ std::regex | JavaScript |
|---|---|---|
^/$ | 只匹配整个序列的开头 / 结尾 | 配合m标志可匹配行首行尾 |
. | 不匹配换行\n | 配合s标志可匹配换行 |
\w\d\s | 区域敏感 | 固定 ASCII |
| 捕获组上限 | 31 个 | 无硬性上限 |
| 匹配策略 | 首次匹配 | 首次匹配 |
| 性能 | 较差(无 DFA 优化) | 引擎优化较好 |
⚠️性能提示:
std::regex在多数实现中性能显著低于 PCRE / RE2 / JavaScript 引擎。
长文本、复杂正则、循环调用场景建议改用RE2、PCRE2或 C++17 的std::regex替代品
(如CTRE编译期正则)。
八、一页速查表
锚点 ^ $ 只匹配整个序列的首尾 普通字符 a . . 不匹配换行 字符集合 [...] [^...] ECMAScript 下 ] 必须写成 \] 字符类 \d \D \s \S \w \W 区域敏感 POSIX 类 [[:alpha:]] 等 仅括号表达式内可用 分组 (...) (?:...) 反向引用 \n \nn 无上限;捕获组最多 31 个 先行断言 (?=...) (?!...) 无后行断言 边界 \b \B \b 是边界,不是退格 转义 \xhh \uhhhh \ck \f \n \r \t \v 量词 * + ? {n} {n,} {n,m} 非贪婪 量词后加 ? 分支 a|b 首次匹配,非最长匹配 替换 $& $$ $1 $2 $nn $` $'常用代码骨架
#include<regex>#include<string>#include<iostream>intmain(){std::string text="订单号 A1234,金额 56.78 元";// 1) 搜索std::regexre(R"([A-Z](\d+))");std::smatch m;if(std::regex_search(text,m,re)){std::cout<<"整个匹配: "<<m.str()<<'\n';// A1234std::cout<<"捕获组 1: "<<m[1].str()<<'\n';// 1234std::cout<<"位置: "<<m.position()<<'\n';}// 2) 完全匹配std::regexre_full(R"(\d{4}-\d{2}-\d{2})");std::cout<<std::boolalpha<<std::regex_match("2026-09-18",re_full)<<'\n';// true// 3) 替换std::regexre_date(R"((\d{4})-(\d{2})-(\d{2}))");std::cout<<std::regex_replace("2026-09-18",re_date,"$1/$2/$3")<<'\n';// 4) 全局遍历std::regexre_num(R"(\d+(\.\d+)?)");autobegin=std::sregex_iterator(text.begin(),text.end(),re_num);autoend=std::sregex_iterator();for(autoit=begin;it!=end;++it){std::cout<<"找到: "<<it->str()<<'\n';}}九、参考资料
- ECMA-262 标准(ECMAScript 3 起定义了正则文法)
- Regular Expressions (C++) — Microsoft Learn(C++ std 库文法对照,最完整的实现级说明)
- MDN RegExp Guide(JavaScript 侧语法对照)
- cppreference: std::regex(若可访问)
说明:本参考以 C++ 标准库(
std::basic_regex的 ECMAScript 文法)为准。
撰写时 cppreference 全站返回 403(Cloudflare 验证页),故以微软官方 std 库文档为主要依据,
该文档描述的即为同一套std::regexECMAScript 行为,并在实现细节上更细
(捕获组上限、]转义差异、标识转义白名单等)。