Namespaces
Variants

Modified ECMAScript regular expression grammar

From cppreference.net

本页描述了当 std::basic_regex 构造时 syntax_option_type 设置为 ECMAScript (默认值)时所使用的正则表达式语法。关于其他支持的正则表达式语法,请参阅 syntax_option_type

C++ 中的 ECMAScript 3 正则表达式语法基于 ECMA-262 语法 ,并包含下方标有 (C++ only) 的修改项。

目录

概述

修改后的 正则表达式语法 主要基于ECMAScript正则表达式语法,并在 ClassAtom 下对区域设置进行了POSIX风格的扩展。同时明确了相等性检查和数字解析的规则。对于本文中的许多示例,您可以在浏览器控制台中尝试以下等效代码:

function match(s, re) { return s.match(new RegExp(re)); }

标准中的“规范性引用”指定了ECMAScript 3。我们在此链接到ECMAScript 5.1规范,因为这是与ECMAScript 3仅有细微差异的版本,并且提供了HTML版本。有关该方言特性的概述,请参阅 MDN关于JavaScript正则表达式的指南

替代方案

正则表达式模式是由一个或多个 候选项 组成的序列,这些候选项通过析取运算符 | 分隔(换言之,析取运算符具有最低优先级)。

模式 ::

析取

析取 ::

替代方案
替代方案 | 析取

该模式首先尝试跳过 析取 并匹配左侧的 备选项 ,然后匹配正则表达式的剩余部分(析取之后的内容)。

如果失败,它会尝试跳过左侧的 Alternative 并匹配右侧的 Disjunction (后跟正则表达式的其余部分)。

如果左侧 Alternative 、右侧 Disjunction 以及正则表达式剩余部分均包含选择点,则会在转向左侧 Alternative 的下一个选择之前,先尝试剩余表达式中的所有选择。当左侧 Alternative 的选择耗尽时,将转而尝试右侧 Disjunction 而非左侧 Alternative

任何在被跳过的 备选项 内部的捕获括号都会生成空子匹配。

#include <cstddef>
#include <iostream>
#include <regex>
#include <string>
void show_matches(const std::string& in, const std::string& re)
{
    std::smatch m;
    std::regex_search(in, m, std::regex(re));
    if (!m.empty())
    {
        std::cout << "input=[" << in << "], regex=[" << re << "]\n  "
                     "prefix=[" << m.prefix() << "]\n  smatch: ";
        for (std::size_t n = 0; n < m.size(); ++n)
            std::cout << "m[" << n << "]=[" << m[n] << "] ";
        std::cout << "\n  suffix=[" << m.suffix() << "]\n";
    }
    else
        std::cout << "input=[" << in << "], regex=[" << re << "]: NO MATCH\n";
}
int main()
{
    show_matches("abcdef", "abc|def");
    show_matches("abc", "ab|abc"); // 左侧候选项首先匹配
    // 输入与左侧候选项(a)匹配后,继续匹配正则表达式的剩余部分(c|bc)成功
    // 导致 m[1]="a" 和 m[4]="bc"
    // 被跳过的候选项(ab)和(c)使其子匹配 m[3] 和 m[5] 为空
    show_matches("abc", "((a)|(ab))((c)|(bc))");
}

输出:

input=[abcdef], regex=[abc|def]
  prefix=[]
  smatch: m[0]=[abc]
  suffix=[def]
input=[abc], regex=[ab|abc]
  prefix=[]
  smatch: m[0]=[ab]
  suffix=[c]
input=[abc], regex=[((a)|(ab))((c)|(bc))]
  prefix=[]
  smatch: m[0]=[abc] m[1]=[a] m[2]=[a] m[3]=[] m[4]=[bc] m[5]=[] m[6]=[bc]
  suffix=[]

术语

每个 Alternative 要么为空,要么是由 Term 组成的序列( Term 之间没有分隔符)

替代 ::

[空]
替代 术语

Alternative 始终匹配且不消耗任何输入。

连续的 Term 会尝试同时匹配输入中的连续部分。

如果左侧 备选项 、右侧 术语项 以及正则表达式剩余部分均存在选择点,系统会先尝试表达式剩余部分的所有选择,再转向右侧 术语项 的下一个选择,而右侧 术语项 的所有选择尝试完毕后,才会转向左侧 备选项 的下一个选择。

#include <cstddef>
#include <iostream>
#include <regex>
#include <string>
void show_matches(const std::string& in, const std::string& re)
{
    std::smatch m;
    std::regex_search(in, m, std::regex(re));
    if (!m.empty())
    {
        std::cout << "input=[" << in << "], regex=[" << re << "]\n  "
                     "prefix=[" << m.prefix() << "]\n  smatch: ";
        for (std::size_t n = 0; n < m.size(); ++n)
            std::cout << "m[" << n << "]=[" << m[n] << "] ";
        std::cout << "\n  suffix=[" << m.suffix() << "]\n";
    }
    else
        std::cout << "input=[" << in << "], regex=[" << re << "]: 无匹配\n";
}
int main()
{
    show_matches("abcdef", ""); // 空正则表达式是单个空候选项
    show_matches("abc", "abc|"); // 左侧候选项首先匹配
    show_matches("abc", "|abc"); // 左侧候选项首先匹配,使 abc 未被匹配
}

输出:

input=[abcdef], regex=[]
  prefix=[]
  smatch: m[0]=[]
  suffix=[abcdef]
input=[abc], regex=[abc|]
  prefix=[]
  smatch: m[0]=[abc]
  suffix=[]
input=[abc], regex=[|abc]
  prefix=[]
  smatch: m[0]=[]
  suffix=[abc]

量词

  • 每个 Term 要么是一个 Assertion (见下文),要么是一个 Atom (见下文),要么是一个 Atom 后紧跟一个 Quantifier

术语 ::

断言
原子
原子 量词

每个 量词 要么是 贪婪 量词(仅包含一个 量词前缀 ),要么是 非贪婪 量词(包含一个 量词前缀 后接问号 ? )。

量词 ::

量词前缀
量词前缀 ?

每个 QuantifierPrefix 会确定两个数值:最小重复次数和最大重复次数,具体对应关系如下:

量词前缀 最小值 最大值
* 无穷大
+ 无穷大
?
{ DecimalDigits } DecimalDigits的值 DecimalDigits的值
{ DecimalDigits , } DecimalDigits的值 无穷大
{ DecimalDigits , DecimalDigits } 逗号前的DecimalDigits值 逗号后的DecimalDigits值

各个 DecimalDigits 的值是通过对每个数字调用 std::regex_traits::value (仅限C++) 获得的。

一个 原子 后接一个 量词 时,该原子会按照量词指定的次数重复匹配。量词可以是 非贪婪模式 ——此时原子模式会以最少重复次数仍能匹配正则表达式剩余部分的方式执行;也可以是 贪婪模式 ——此时原子模式会以最多重复次数仍能匹配正则表达式剩余部分的方式执行。

Atom 模式是被重复的部分,而非其匹配的输入内容,因此不同的 Atom 重复可以匹配不同的输入子字符串。

如果 Atom 和正则表达式剩余部分均存在选择点,首先将 Atom 匹配尽可能多(若为 非贪婪模式 则尽可能少)的次数。在转向 Atom 最后一次重复中的下一个选择之前,会先尝试正则表达式剩余部分的所有选择。在转向倒数第二次(n–1次) Atom 重复中的下一个选择之前,会先尝试最后一次(第n次) Atom 重复中的所有选择;此时可能会发现现在可以匹配更多或更少的 Atom 重复次数;这些可能性将被全部尝试(再次从尽可能少或尽可能多开始),然后才会转向倒数第二次(n-1次) Atom 重复中的下一个选择,依此类推。

Atom' 的捕获在每次重复时都会被清除(参见下面的 "(z)((a+)?(b+)?(c))*" 示例)

#include <cstddef>
#include <iostream>
#include <regex>
#include <string>
void show_matches(const std::string& in, const std::string& re)
{
    std::smatch m;
    std::regex_search(in, m, std::regex(re));
    if (!m.empty())
    {
        std::cout << "input=[" << in << "], regex=[" << re << "]\n  "
                     "prefix=[" << m.prefix() << "]\n  smatch: ";
        for (std::size_t n = 0; n < m.size(); ++n)
            std::cout << "m[" << n << "]=[" << m[n] << "] ";
        std::cout << "\n  suffix=[" << m.suffix() << "]\n";
    }
    else
        std::cout << "input=[" << in << "], regex=[" << re << "]: 无匹配\n";
}
int main()
{
    // 贪婪匹配,重复 [a-z] 4 次
    show_matches("abcdefghi", "a[a-z]{2,4}");
    // 非贪婪匹配,重复 [a-z] 2 次
    show_matches("abcdefghi", "a[a-z]{2,4}?");
    // 量词的选择点排序导致匹配结果包含两次重复:
    // 第一次匹配子串 "aa",第二次匹配子串 "ba",
    // 留下 "ac" 未匹配("ba" 出现在捕获组 m[1] 中)
    show_matches("aabaac", "(aa|aabaac|ba|b|c)*");
    // 量词的选择点排序使此正则表达式计算 10 和 15 的最大公约数
    //(结果为 5,并在 m[1] 中填充 "aaaaa")
    show_matches("aaaaaaaaaa,aaaaaaaaaaaaaaa", "^(a+)\\1*,\\1+$");
    // 子串 "bbb" 未出现在捕获组 m[4] 中,
    // 因为当原子 (a+)?(b+)?(c) 的第二次重复匹配子串 "ac" 时,该组被清空
    // 注意:gcc 对此处理有误 - 未按 ECMA-262 21.2.2.5.1 要求正确清空
    // matches[4] 捕获组,因此错误地捕获了 "bbb"
    show_matches("zaacbbbcac", "(z)((a+)?(b+)?(c))*");
}

输出:

input=[abcdefghi], regex=[a[a-z]{2,4}]
  prefix=[]
  smatch: m[0]=[abcde]
  suffix=[fghi]
input=[abcdefghi], regex=[a[a-z]{2,4}?]
  prefix=[]
  smatch: m[0]=[abc]
  suffix=[defghi]
input=[aabaac], regex=[(aa|aabaac|ba|b|c)*]
  prefix=[]
  smatch: m[0]=[aaba] m[1]=[ba]
  suffix=[ac]
input=[aaaaaaaaaa,aaaaaaaaaaaaaaa], regex=[^(a+)\1*,\1+$]
  prefix=[]
  smatch: m[0]=[aaaaaaaaaa,aaaaaaaaaaaaaaa] m[1]=[aaaaa]
  suffix=[]
input=[zaacbbbcac], regex=[(z)((a+)?(b+)?(c))*]
  prefix=[]
  smatch: m[0]=[zaacbbbcac] m[1]=[z] m[2]=[ac] m[3]=[a] m[4]=[] m[5]=[c] 
  suffix=[]

断言

断言 匹配的是条件,而非输入字符串的子串。它们从不消耗输入中的任何字符。每个 断言 为以下之一:

断言 ::

^
$
\ b
\ B
( ? = 析取表达式 )
( ? ! 析取表达式 )

断言 ^ (行首) 匹配

1) 紧跟在 LineTerminator 字符之后的位置 (此功能可能不受支持) (C++17 前) (仅当启用 std::regex_constants::multiline (仅限 C++) 时保证生效) (C++17 起)
2) 输入序列的起始位置(除非启用了 std::regex_constants::match_not_bol (仅限C++)

断言 $ (行尾)匹配

1) LineTerminator 字符的位置 (此功能可能不被支持) (C++17 前) (仅当启用 std::regex_constants::multiline (C++ only) 时保证有效) (C++17 起)
2) 输入结束(除非启用了 std::regex_constants::match_not_eol (C++ only)

在上述两个断言以及下面的原子符号 . 中, LineTerminator 是以下四个字符之一: U+000A \n 或换行符)、 U+000D \r 或回车符)、 U+2028 (行分隔符)或 U+2029 (段落分隔符)

断言 \b (单词边界)匹配

1) 单词的起始位置(当前字符是字母、数字或下划线,且前一个字符不是)
2) 单词的结尾(当前字符不是字母、数字或下划线,且前一个字符是这些字符之一)
3) 输入开始位置,当首字符为字母、数字或下划线时(除非启用了 std::regex_constants::match_not_bow (C++ only)
4) 输入结束,当最后一个字符是字母、数字或下划线时(除非启用了 std::regex_constants::match_not_eow (仅限C++)

断言 \B (负向单词边界)匹配除以下情况外的所有内容

1) 单词的起始位置(当前字符是字母、数字或下划线,且前一个字符不是这些字符之一或不存在)
2) 单词的结尾(当前字符不是字母、数字或下划线(或匹配器位于输入末尾),且前一个字符是上述字符之一)

断言 ( ? = Disjunction ) (零宽度正向先行断言)在当前输入位置匹配的条件是: Disjunction 能够在此位置匹配

断言 ( ? ! Disjunction ) (零宽度负向先行断言)在当前输入位置处,当 Disjunction 无法匹配时成功匹配。

对于两种向前查找断言,当匹配 Disjunction 时,在匹配正则表达式剩余部分之前不会前进匹配位置。此外,如果 Disjunction 在当前位置存在多种匹配方式,仅会尝试第一种匹配方式。

ECMAScript 禁止回溯到前视析取中,这会影响从正则表达式剩余部分反向引用到正向先行断言的行为(参见下方示例)。从正则表达式其余部分反向引用到负向先行断言的结果始终未定义(因为前视析取必须失败才能继续执行)。

注意:前视断言可用于在多个正则表达式之间创建逻辑与关系(参见下方示例)。

#include <cstddef>
#include <iostream>
#include <regex>
#include <string>
void show_matches(const std::string& in, const std::string& re)
{
    std::smatch m;
    std::regex_search(in, m, std::regex(re));
    if (!m.empty())
    {
        std::cout << "input=[" << in << "], regex=[" << re << "]\n  "
                     "prefix=[" << m.prefix() << "]\n  smatch: ";
        for (std::size_t n = 0; n < m.size(); ++n)
            std::cout << "m[" << n << "]=[" << m[n] << "] ";
        std::cout << "\n  suffix=[" << m.suffix() << "]\n";
    }
    else
        std::cout << "input=[" << in << "], regex=[" << re << "]: NO MATCH\n";
}
int main()
{
    // 匹配输入末尾的 a
    show_matches("aaa", "a$");
    // 匹配第一个单词末尾的 o
    show_matches("moo goo gai pan", "o\\b");
    // 正向先行断言在第一个 b 之后立即匹配空字符串
    // 虽然 m[0] 为空,但 m[1] 被填充为 "aaa"
    show_matches("baaabac", "(?=(a+))");
    // 由于禁止回溯到正向先行断言中,
    // 此处匹配 aba 而非 aaaba
    show_matches("baaabac", "(?=(a+))a*b\\1");
    // 通过正向先行断言实现逻辑与:此密码需满足以下条件才匹配
    // 至少包含一个小写字母
    // 且至少包含一个大写字母
    // 且至少包含一个标点符号
    // 且长度至少为 6 个字符
    show_matches("abcdef", "(?=.*[[:lower:]])(?=.*[[:upper:]])(?=.*[[:punct:]]).{6,}");
    show_matches("aB,def", "(?=.*[[:lower:]])(?=.*[[:upper:]])(?=.*[[:punct:]]).{6,}");
}

输出:

input=[aaa], regex=[a$]
  prefix=[aa]
  smatch: m[0]=[a] 
  suffix=[]
input=[moo goo gai pan], regex=[o\b]
  prefix=[mo]
  smatch: m[0]=[o] 
  suffix=[ goo gai pan]
input=[baaabac], regex=[(?=(a+))]
  prefix=[b]
  smatch: m[0]=[] m[1]=[aaa] 
  suffix=[aaabac]
input=[baaabac], regex=[(?=(a+))a*b\1]
  prefix=[baa]
  smatch: m[0]=[aba] m[1]=[a] 
  suffix=[c]
input=[abcdef], regex=[(?=.*[[:lower:]])(?=.*[[:upper:]])(?=.*[[:punct:]]).{6,}]: NO MATCH
input=[aB,def], regex=[(?=.*[[:lower:]])(?=.*[[:upper:]])(?=.*[[:punct:]]).{6,}]
  prefix=[]
  smatch: m[0]=[aB,def] 
  suffix=[]

原子

一个 原子 可以是以下类型之一:

原子 ::

模式字符
.
\ 原子转义
字符类
( 析取 )
( ? : 析取 )

其中 AtomEscape ::

DecimalEscape
CharacterEscape
CharacterClassEscape

不同种类的原子具有不同的求值方式。

子表达式

Atom ( Disjunction ) 是一个标记子表达式:它执行 Disjunction 并将 Disjunction 所消耗的输入子串副本存储在子匹配数组中,其索引对应于此时在整个正则表达式中遇到标记子表达式的左开括号 ( 的次数。

除了在 std::match_results 中返回之外,捕获的子匹配项还可以作为反向引用( \1 \2 等)进行访问,并可在正则表达式中引用。请注意, std::regex_replace 使用 $ 而非 \ 表示反向引用( $1 $2 等),其使用方式与 String.prototype.replace (ECMA-262第15.5.4.11节)相同。

Atom ( ? : Disjunction ) (非标记子表达式)仅对 Disjunction 进行求值,且不会将其结果存储到子匹配中。这是一种纯粹的词法分组。

反向引用

DecimalEscape ::

DecimalIntegerLiteral [ lookahead DecimalDigit ]

如果 \ 后跟一个首位非 0 的十进制数字 N ,则该转义序列被视为 反向引用 。数值 N 通过对每个数字调用 std::regex_traits::value (仅限 C++) 并采用十进制算术组合其结果而获得。若 N 大于整个正则表达式中左捕获括号的总数,则视为错误。

当反向引用 \N 作为 原子 出现时,它会匹配当前存储在子匹配数组第 N 个元素中的相同子字符串。

十进制转义符 \0 并非反向引用:它是一个表示 NUL 字符的字符转义符。其后不能紧跟十进制数字。

如前所述,请注意 std::regex_replace 使用 $ 而非 \ 作为反向引用标识符( $1 $2 等)。

单字符匹配

Atom . 匹配并消耗输入字符串中除 行终止符 U+000D U+000A U+2029 U+2028 )之外的任意单个字符

除了字符 ^ $ \ . * + ? ( ) [ ] { } | 之外的任何 源字符 所构成的 原子模式字符 ,若输入字符与该 模式字符 相等,则匹配并消耗输入中的一个字符。

该字符及所有其他单字符匹配的相等性定义如下:

1) 若设置了 std::regex_constants::icase ,则当 std::regex_traits::translate_nocase 的返回值相等时,字符被视为等同 (仅限 C++)
2) 否则,若设置了 std::regex_constants::collate ,则当 std::regex_traits::translate 的返回值相等时字符相等 (仅限 C++)
3) 否则,当 operator == 返回 true 时,字符被视为相等。

每个由转义字符 \ 后接 CharacterEscape 组成的 Atom ,以及特殊的十进制转义序列 \0 ,当输入字符与 CharacterEscape 所表示的字符相等时,将匹配并消耗输入中的一个字符。可识别的字符转义序列包括:

CharacterEscape ::

ControlEscape
c ControlLetter
HexEscapeSequence
UnicodeEscapeSequence
IdentityEscape

这里, ControlEscape 是以下五个字符之一: f n r t v

ControlEscape 代码单元 名称
f U+000C 换页符
n U+000A 换行符
r U+000D 回车符
t U+0009 水平制表符
v U+000B 垂直制表符

ControlLetter 可以是任意小写或大写 ASCII 字母,该转义字符匹配的代码单元值等于 ControlLetter 代码单元值除以 32 的余数。例如, \cD \cd 均匹配代码单元 U+0004 (EOT),因为 'D' 对应 U+0044 0x44 % 32 == 4 ,而 'd' 对应 U+0064 0x64 % 32 == 4

HexEscapeSequence 是字母 x 后跟恰好两个 HexDigit (其中 HexDigit 0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F 之一)。该字符转义匹配其码元等于这个两位十六进制数值所对应的字符。

UnicodeEscapeSequence 是由字母 u 后跟恰好四个 HexDigit 组成的序列。该字符转义匹配其码元值等于这个四位十六进制数数值的字符。如果该数值不符合此 std::basic_regex CharT 类型,将抛出 std::regex_error (仅限 C++)

IdentityEscape 可以是任何非字母数字字符:例如,另一个反斜杠。它会按原样匹配该字符。

#include <cstddef>
#include <iostream>
#include <regex>
#include <string>
void show_matches(const std::wstring& in, const std::wstring& re)
{
    std::wsmatch m;
    std::regex_search(in, m, std::wregex(re));
    if (!m.empty())
    {
        std::wcout << L"input=[" << in << L"], regex=[" << re << L"]\n  "
                      L"prefix=[" << m.prefix() << L"]\n  wsmatch: ";
        for (std::size_t n = 0; n < m.size(); ++n)
            std::wcout << L"m[" << n << L"]=[" << m[n] << L"] ";
        std::wcout << L"\n  suffix=[" << m.suffix() << L"]\n";
    }
    else
        std::wcout << L"input=[" << in << "], regex=[" << re << L"]: NO MATCH\n";
}
int main()
{
    // 大多数转义字符与C++类似,除了元字符。对于斜杠,您需要双重转义或使用原始字符串。
    show_matches(L"C++\\", LR"(C\+\+\\)");
    // 转义序列与NUL字符。
    std::wstring s(L"ab\xff\0cd", 5);
    show_matches(s, L"(\\0|\\u00ff)");
    // 未定义非BMP Unicode的匹配,因为ECMAScript使用UTF-16原子。
    // 这个表情符号香蕉是否匹配可能依赖于平台:
    // 这些需要使用宽字符串!
    show_matches(L"\U0001f34c", L"[\\u0000-\\ufffe]+");
}

可能的输出:

input=[C++\], regex=[C\+\+\\]
  prefix=[]
  wsmatch: m[0]=[C++\]
  suffix=[]
input=[ab?c], regex=[(\0{{!}}\u00ff)]
  prefix=[ab]
  wsmatch: m[0]=[?] m[1]=[?]
  suffix=[c]
input=[?], regex=[[\u0000-\ufffe]+]: NO MATCH

字符类

一个Atom可以表示一个字符类,即当字符属于预定义字符组之一时,它将匹配并消耗一个字符。

字符类可以通过字符类转义引入:

原子 ::

\ 字符类转义

或直接

原子 ::

字符类

字符类转义是某些常见字符类的简写形式,具体如下:

字符类转义 类名表达式 (仅限C++) 含义
d [[:digit:]] 数字字符
D [^[:digit:]] 非数字字符
s [[:space:]] 空白字符
S [^[:space:]] 非空白字符
w [_[:alnum:]] 字母数字字符及字符 _
W [^_[:alnum:]] 除字母数字和 _ 外的字符
C++中这些字符类转义的确切含义是通过区域设置相关的命名字符类来定义的,而不是像ECMAScript那样通过明确列出可接受字符来定义。

一个 字符类 是由括号包围的 类范围 序列,可选择以取反运算符 ^ 开头。若以 ^ 起始,该 原子 将匹配任何不属于所有 类范围 并集所表示字符集合的字符。否则,该 原子 将匹配任何属于所有 类范围 并集所表示字符集合中的字符。

CharacterClass ::

[ [ 前瞻字符 ∉ { ^ } 字符类范围 ]
[ ^ 字符类范围 ]

类范围 ::

[空]
非空类范围

非空类范围 ::

类原子
类原子 非空类范围无连字符
类原子 - 类原子 类范围

如果非空字符类范围具有形式 ClassAtom - ClassAtom ,它将匹配按如下方式定义的字符范围: (仅限 C++)

第一个 ClassAtom 必须匹配单个对照元素 c1 ,第二个 ClassAtom 必须匹配单个对照元素 c2 。要测试输入字符 c 是否被此范围匹配,需执行以下步骤:

1) 如果未启用 std::regex_constants::collate ,则通过直接比较码点来匹配字符:当 c1 <= c && c <= c2 时匹配 c
1) 否则(若启用了 std::regex_constants::collate ):
1) 若启用了 std::regex_constants::icase ,则三个字符( c c1 c2 )均会传入 std::regex_traits::translate_nocase
2) 否则(若未设置 std::regex_constants::icase ),所有三个字符( c c1 c2 )都将被传递至 std::regex_traits::translate
2) 生成的字符串通过 std::regex_traits::transform 进行比较,当满足 transformed c1 <= transformed c && transformed c <= transformed c2 条件时,字符 c 即匹配成功

字符 - 在以下情况下会被按字面意义处理:

  • ClassRanges 的首个或末尾字符
  • 短横线分隔范围规范的起始或结束 ClassAtom
  • 紧跟在短横线分隔范围规范之后
  • 使用反斜杠作为 CharacterEscape 进行转义

非空类范围无破折号 ::

类原子
无连字符类原子 非空无连字符类范围
无连字符类原子 - 类原子 类范围

ClassAtom ::

-
ClassAtomNoDash
ClassAtomExClass (仅限 C++)
ClassAtomCollatingElement (仅限 C++)
ClassAtomEquivalence (仅限 C++)

ClassAtomNoDash ::

SourceCharacter 但不包括 \ 或 ] 或 -
\ ClassEscape

每个 ClassAtomNoDash 代表单个字符——可以是原样的 SourceCharacter ,或按如下方式转义:

类转义 ::

十进制转义
b
字符转义
字符类转义

特殊的 ClassEscape \b 生成匹配代码单元 U+0008(退格符)的字符集。在 CharacterClass 外部,它是单词边界 Assertion

在字符类中使用 \B 以及使用任何反向引用(除零之外的十进制转义)都是错误的。

字符 - ] 在某些情况下可能需要转义才能被视为原子。在 字符类 外具有特殊含义的其他字符(例如 * ? )则无需转义。

基于POSIX的字符类

这些字符类是ECMAScript语法的扩展,与POSIX正则表达式中的字符类等效。

ClassAtomExClass (C++ only) ::

[: 类名 :]

表示属于命名字符类 ClassName 的所有字符。仅当 std::regex_traits::lookup_classname 对该名称返回非零值时,该名称才有效。如 std::regex_traits::lookup_classname 中所述,以下名称保证被识别: alnum, alpha, blank, cntrl, digit, graph, lower, print, punct, space, upper, xdigit, d, s, w 。系统提供的区域设置(如日语中的 jdigit jkanji )可能提供其他名称,或通过用户自定义扩展实现。

ClassAtomCollatingElement (C++ only) ::

[. 类名 .]

表示命名的排序元素,该元素可能代表单个字符或在当前语言环境下作为单个单元进行排序的字符序列,例如捷克语中的 [.tilde.] [.ch.] 。仅当 std::regex_traits::lookup_collatename 返回非空字符串时,该名称才有效。

当使用 std::regex_constants::collate 时,排序元素始终可用作范围的端点(例如匈牙利语中的 [[.dz.]-g] )。

类原子等价性 (仅限C++) ::

[= 类名 =]

表示与指定排序元素属于同一等价类的所有字符,即所有主排序键与排序元素 ClassName 相同的字符。该名称仅在以下情况下有效: std::regex_traits::lookup_collatename 对该名称的调用返回非空字符串,且通过 std::regex_traits::transform_primary std::regex_traits::lookup_collatename 调用结果进行转换后的返回值也为非空字符串。

主排序键是一种忽略大小写、重音符号或区域特定定制化的键;例如 [[=a=]] 会匹配以下任意字符: a, À, Á, Â, Ã, Ä, Å, A, à, á, â, ã, ä and å。

类名 (仅限C++) ::

类名字符
类名字符 类名

类名字符 (仅限C++) ::

SourceCharacter 但不包括 . = : 中的字符