std::regex ECMAScript 语法完整参考C 中std::regex默认使用的正则语法std::regex_constants::ECMAScript。该语法基于ECMAScript 3ECMA-262 第 3 版并在此基础上做了少量修改与限制。目录一、语法选择与标志二、核心文法解析三、元素详解1. 字符类 Character Classes2. 量词 Quantifiers3. 断言 Assertions4. 分组与反向引用5. 转义序列四、括号表达式细节五、匹配语义六、替换格式七、C 特有的修改与限制八、一页速查表九、参考资料一、语法选择与标志#includeregexusingnamespacestd::regex_constants;可选语法syntax_option_type取值说明ECMAScript默认。最接近 JavaScript / .NET 语言的语法basicPOSIX 基本正则BREextendedPOSIX 扩展正则EREawkextended 更多非打印字符转义grepbasic 允许\n分隔分支egrepextended 允许\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 falseC 中通过std::smatch检查std::string sa;std::smatch m;std::regexre(R((a)|(b)));if(std::regex_search(s,m,re)){m[1].matched;// true, m[1] am[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_]ASCIIPOSIX 具名类[: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*// 匹配 aaaa\*// 匹配字面量 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// 贪婪 组1aaa, 组2b在量词后加?可变为非贪婪模式(a?)(a*b)// 目标 aaab// 非贪婪组1a, 组2aab可用于量词的元素除(?、(?!、^、$之外的所有元素。以下示例均按整串匹配std::regex_match语义理解。a{2,3}// 匹配 aa / aaa不匹配 a / aaaaa{2}// 只匹配 aaa{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// 不匹配 aa\b.// 匹配 a~不匹配 aba\B.// 匹配 ab不匹配 a~(?!aa)(a*)// 匹配 a组1a不匹配 aa / aaa(?aa)(a*)// 匹配 aaaa组1aaaa⚠️\b在 ECMAScript 下是单词边界断言不是退格符。退格符的写法见 5. 转义序列。4. 分组与反向引用语法含义(...)捕获组将子模式分组并捕获匹配的文本(?:...)非捕获组只分组不捕获文本\n反向引用引用第 n 个捕获组匹配到的文本捕获组编号由左括号的出现顺序决定数到该左括号为止的开括号个数。((a)(b))(c)// 组1 aabbb// 组2 aa// 组3 bbb// 组4 c反向引用的编号解析差异重要语法N 的确定方式上限basic/grep只取反斜杠后的一位十进制数字N ≤ 9ECMAScript取反斜杠后连续的所有十进制数字无上限((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 作用于整个 ab5. 转义序列类型形式含义标识转义\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// 匹配 \x09CtrlI 0x09\x41// ASCII 下匹配 a\u0041// ASCII 下匹配 a\.\.\*\\?\(\)\[\]\{\}\|\^\$ \\C 中的字符串字面量陷阱正则里的\在 C 字符串字面量中本身需要转义否则编译器会先吃掉一层。建议一律用原始字符串字面量Raw String Literalstd::regexbad(R(\d));// ✅ 正确正则收到 \dstd::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 }因为 h0x88, k0x92使用collate标志时范围内的字符由 locale 的排序规则决定。五、匹配语义regex_matchvsregex_search要求示例std::regex_match整个正则匹配整个目标序列bcd不匹配abcd也不匹配bcdestd::regex_search目标中存在子序列匹配即可bcd能匹配abcd、bcdebcd 在abcd中 search → 匹配后三个字符 bcd 在bcde中 search → 匹配前三个字符 bcd 在bcdbcd中 search → 匹配前三个字符最左首次匹配 vs 最长匹配同一位置存在多个匹配时的选择策略策略规则首次匹配First MatchECMAScript 使用。取正则匹配过程中先找到的那个最长匹配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–99std::string s2026-09-18;std::regexre(R((\d{4})-(\d{2})-(\d{2})));// 换成 年/月/日std::string outstd::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...)具名捕获组✅\kname具名反向引用✅\p{...}Unicode 属性类✅\Q...\E引用块❌PCRE 支持x/x*占有量词❌PCRE/Java 支持(?(cond)...)条件表达式❌PCRE 支持4. 语法选项互斥ECMAScript是默认文法在basic、extended、awk、grep、egrep等选项中最多只能选择一种。5. 其他行为差异项目C std::regexJavaScript^/$只匹配整个序列的开头 / 结尾配合m标志可匹配行首行尾.不匹配换行\n配合s标志可匹配换行\w\d\s区域敏感固定 ASCII捕获组上限31 个无硬性上限匹配策略首次匹配首次匹配性能较差无 DFA 优化引擎优化较好⚠️性能提示std::regex在多数实现中性能显著低于 PCRE / RE2 / JavaScript 引擎。长文本、复杂正则、循环调用场景建议改用RE2、PCRE2或 C17 的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 $ $常用代码骨架#includeregex#includestring#includeiostreamintmain(){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::coutstd::boolalphastd::regex_match(2026-09-18,re_full)\n;// true// 3) 替换std::regexre_date(R((\d{4})-(\d{2})-(\d{2})));std::coutstd::regex_replace(2026-09-18,re_date,$1/$2/$3)\n;// 4) 全局遍历std::regexre_num(R(\d(\.\d)?));autobeginstd::sregex_iterator(text.begin(),text.end(),re_num);autoendstd::sregex_iterator();for(autoitbegin;it!end;it){std::cout找到: it-str()\n;}}九、参考资料ECMA-262 标准ECMAScript 3 起定义了正则文法Regular Expressions (C) — Microsoft LearnC std 库文法对照最完整的实现级说明MDN RegExp GuideJavaScript 侧语法对照cppreference: std::regex若可访问说明本参考以 C 标准库std::basic_regex的 ECMAScript 文法为准。撰写时 cppreference 全站返回 403Cloudflare 验证页故以微软官方 std 库文档为主要依据该文档描述的即为同一套std::regexECMAScript 行为并在实现细节上更细捕获组上限、]转义差异、标识转义白名单等。