正则表达式c++内容汇集

std::regex ECMAScript 语法完整参考

C++ 中 std::regex 默认使用的正则语法(std::regex_constants::ECMAScript)。

该语法基于 ECMAScript 3(ECMA-262 第 3 版),并在此基础上做了少量修改与限制。


目录


一、语法选择与标志

cpp 复制代码
#include <regex>
using namespace std::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。

cpp 复制代码
std::regex re1(R"(\d+)", std::regex::ECMAScript);
std::regex re2(R"(abc)", std::regex::icase | std::regex::ECMAScript);
std::regex re3(R"(abc)");   // 默认即 ECMAScript

二、核心文法解析

一个正则表达式文法由多个可选项(Alternative)组成,各项之间用 析取运算符 | 分隔。

匹配优先级

| 的优先级最低 。匹配时引擎会优先尝试匹配左侧的可选项;只有左侧失败,才会尝试右侧。

cpp 复制代码
b|bc    // 在 "abcd" 中 search
        //  → 匹配 "b"(左项先成功,不再尝试右项 "bc")

这就是 ECMAScript 的**「首次匹配」**语义,与 POSIX 的「最长匹配」不同。详见 五、匹配语义。

捕获行为

若左侧可选项匹配成功,则右侧可选项中定义的捕获组会产生「空子匹配」(empty submatches)。

cpp 复制代码
(a)|(b)    // 匹配 "a" 时:
           //   捕获组 1 = "a"
           //   捕获组 2 = 空子匹配(matched = false)

C++ 中通过 std::smatch 检查:

cpp 复制代码
std::string s = "a";
std::smatch m;
std::regex re(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 字母需要自己写字符类。

通配符 .

cpp 复制代码
.        // 匹配目标序列中除换行(\n)以外的任意字符

普通字符(需转义的特殊字符)

ECMAScript 中有特殊含义的字符固定为这 13 个:

复制代码
^  $  \  .  *  +  ?  (  )  [  ]  {  }  |

标识转义(Identity Escape)

反斜杠 + 单个字符,匹配该字符本身,用于去掉特殊含义。

cpp 复制代码
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 次

贪婪与非贪婪

默认情况下量词是贪婪的 ------ 会尽可能多地匹配字符。

cpp 复制代码
(a+)(a*b)    // 目标 "aaab"
             //  贪婪: 组1="aaa", 组2="b"

在量词后加 ? 可变为非贪婪模式:

cpp 复制代码
(a+?)(a*b)   // 目标 "aaab"
             //  非贪婪:组1="a", 组2="aab"

可用于量词的元素 :除 (?=、(?!、^、$ 之外的所有元素。

以下示例均按整串匹配 (std::regex_match)语义理解。

cpp 复制代码
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++ 没有跟进。

单词边界出现的四种情况:

  1. 当前字符在序列开头,且是单词字符(A-Za-z0-9_)
  2. 当前位置越过序列结尾,且最后一个字符是单词字符
  3. 当前字符是单词字符,前一个不是
  4. 当前字符不是单词字符,前一个是

示例:

cpp 复制代码
(?=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. 转义序列](#5. 转义序列)。


4. 分组与反向引用

语法 含义
(...) 捕获组,将子模式分组并捕获匹配的文本
(?:...) 非捕获组,只分组,不捕获文本
\n 反向引用,引用第 n 个捕获组匹配到的文本

捕获组编号:由左括号的出现顺序决定(数到该左括号为止的开括号个数)。

cpp 复制代码
((a+)(b+))(c+)
//  组1 = "aabbb"
//  组2 = "aa"
//  组3 = "bbb"
//  组4 = "c"

反向引用的编号解析差异(重要):

语法 N 的确定方式 上限
basic / grep 只取反斜杠后的一位十进制数字 N ≤ 9
ECMAScript 取反斜杠后连续的所有十进制数字 无上限
cpp 复制代码
((a+)(b+))(c+)\3     // 匹配 "aabbbcbbb"
                     // \3 引用组3 的内容 "(b+)"

(a)\2                // 非法(不存在组2)

(b(((((((((a))))))))))\10
//  ECMAScript:\10 = 第 10 个捕获组(最内层那个)
//  basic:     \1  = 第 1 个捕获组,后面的 0 是普通字符 '0'

非捕获组与反向引用:

cpp 复制代码
(?:a)       // 合法,但不会产生捕获组
(?:a)\1     // 非法 ------ 不存在第 1 个捕获组

⚠️ C++ 实现限制:捕获组最多 31 个。

分组对量词的影响:

以下按整串匹配理解。

cpp 复制代码
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 里才表示响铃)

示例:

cpp 复制代码
"\ci"      // 匹配 "\x09"(Ctrl+I = 0x09)
"\x41"     // ASCII 下匹配 "a"
"\u0041"   // ASCII 下匹配 "a"
\.

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

C++ 中的字符串字面量陷阱:

正则里的 \ 在 C++ 字符串字面量中本身需要转义,否则编译器会先吃掉一层。建议一律用原始字符串字面量(Raw String Literal):

cpp 复制代码
std::regex bad(R"(\d+)");      // ✅ 正确:正则收到 \d+
std::regex wrong("\\d+");      // ✅ 正确但易错
std::regex oops("\d+");        // ❌ 危险:\d 不是合法转义序列,行为未定义

四、括号表达式细节

[expr] 内可包含以下构成的任意组合:

构成 形式 说明
单个字符 abc 将该字符加入集合
字符范围 ch1-ch2 将闭区间 [ch1, ch2] 内的字符加入集合
字符类 [:name:] 将具名类中的字符加入集合
等价类 [=elt=] 将与 elt 等价的排序元素加入集合
排序符号 [.elt.] 将排序元素 elt 加入集合

] 的处理 ------ ECMAScript 与其他语法的分水岭

cpp 复制代码
// 其他语法:位于开头的 ] 表示自身
[]abc]      // 匹配 a b c ]
[^]abc]     // 匹配除 a b c ] 外的字符

// ECMAScript:必须转义
[]a         // 匹配 "a"(空括号表达式 + 普通字符 a)
[\]abc]     // 匹配 a b c ]

^ 的位置

仅当位于括号表达式开头时表示取反,其他位置表示自身。

cpp 复制代码
[abc]      // a 或 b 或 c
[^abc]     // 非 a b c
[a^bc]     // a、b、c 或 ^

- 的位置

在开头、结尾、或作为某个范围的首尾字符时,表示自身。

cpp 复制代码
[0-7]      // { 0,1,2,3,4,5,6,7 }
[-0-24]    // { -, 0, 1, 2, 4 }
[0-2-]     // { 0, 1, 2, - }
[+--]      // { +, ',', - }   (ASCII)

范围依赖平台字符编码

cpp 复制代码
[h-k]
// ASCII:{ h, i, j, k }
// EBCDIC:{ h, i, \x8A, ..., \x90, j, k }(因为 h=0x88, k=0x92)

使用 collate 标志时,范围内的字符由 locale 的排序规则决定。


五、匹配语义

要求 示例
std::regex_match 整个正则匹配整个目标序列 bcd 不匹配 "abcd",也不匹配 "bcde"
std::regex_search 目标中存在子序列匹配即可 bcd 能匹配 "abcd"、"bcde"
cpp 复制代码
bcd  在 "abcd"   中 search → 匹配后三个字符
bcd  在 "bcde"   中 search → 匹配前三个字符
bcd  在 "bcdbcd" 中 search → 匹配前三个字符(最左)

首次匹配 vs 最长匹配

同一位置存在多个匹配时的选择策略:

策略 规则
首次匹配(First Match) ECMAScript 使用 。取正则匹配过程中先找到的那个
最长匹配(Longest Match) 取该位置最长的子序列;长度相同时取先找到的
cpp 复制代码
b|bc   在 "abcd" 中 search
//  ECMAScript(首次匹配)→ "b"
//  最长匹配              → "bc"

部分匹配 Partial Match

匹配到达目标序列末尾但未到达正则末尾,也算成功。

cpp 复制代码
ab   部分匹配 "a"    ✅
ab   部分匹配 "ac"   ❌
  • 部分匹配成功 后,向目标追加字符可能导致后续部分匹配失败
  • 部分匹配失败 后,向目标追加字符不可能导致后续部分匹配成功
cpp 复制代码
std::regex re("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)
cpp 复制代码
std::string s = "2026-09-18";
std::regex re(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 类型扩展。

cpp 复制代码
[[: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  $`  $'

常用代码骨架

cpp 复制代码
#include <regex>
#include <string>
#include <iostream>

int main() {
    std::string text = "订单号 A1234,金额 56.78 元";

    // 1) 搜索
    std::regex re(R"([A-Z](\d+))");
    std::smatch m;
    if (std::regex_search(text, m, re)) {
        std::cout << "整个匹配: " << m.str()  << '\n';   // A1234
        std::cout << "捕获组 1: " << m[1].str() << '\n'; // 1234
        std::cout << "位置: "     << m.position() << '\n';
    }

    // 2) 完全匹配
    std::regex re_full(R"(\d{4}-\d{2}-\d{2})");
    std::cout << std::boolalpha
              << std::regex_match("2026-09-18", re_full) << '\n';  // true

    // 3) 替换
    std::regex re_date(R"((\d{4})-(\d{2})-(\d{2}))");
    std::cout << std::regex_replace("2026-09-18", re_date, "$1/$2/$3") << '\n';

    // 4) 全局遍历
    std::regex re_num(R"(\d+(\.\d+)?)");
    auto begin = std::sregex_iterator(text.begin(), text.end(), re_num);
    auto end   = std::sregex_iterator();
    for (auto it = begin; it != end; ++it) {
        std::cout << "找到: " << it->str() << '\n';
    }
}

九、参考资料

说明 :本参考以 C++ 标准库(std::basic_regex 的 ECMAScript 文法)为准。

撰写时 cppreference 全站返回 403(Cloudflare 验证页),故以微软官方 std 库文档为主要依据,

该文档描述的即为同一套 std::regex ECMAScript 行为,并在实现细节上更细

(捕获组上限、] 转义差异、标识转义白名单等)。

相关推荐
余槐i1 小时前
EXPLAIN 显示走了索引查询仍慢:回表与选择性在 500 万行表上拉开 10 倍耗时
数据库·postgresql·性能优化·vacuum·explain
别动我齐刘海1 小时前
简历技术栈全面复习——UDP / TCP / CAN / ZMQ / Protobuf 通信工程
网络·c++·python·tcp/ip·机器学习·udp·github
深度智能Ai2 小时前
IndexTTS‑2高清音质接口对接文档
数据库
香瓜子rd2 小时前
MySQL InnoDB 并发控制核心原理:事务、隔离级别、MVCC 与锁机制
数据库·后端
All for pursuit.2 小时前
【二分查找-2】34.在排序数组中查找元素的第一个和最后一个位置
数据结构·c++·算法·leetcode
Elastic 中国社区官方博客2 小时前
Elasticsearch Serverless 如何通过 hollow shards 将索引节点关闭次数降低 30%
大数据·数据库·elasticsearch·搜索引擎·serverless·全文检索
大侠归来2 小时前
C语言与C++在基础语法上的区别
java·c语言·c++
看看我呀看看我3 小时前
基于Qt的虚拟键盘
c++·qt·qt5·qt6.3
ycvv3 小时前
手机启动失败后的数据提取评估:系统状态、加密条件与文件验收
服务器·数据库·智能手机