正则表达式工具 |
grep |
语言和函数库 |
Boost |
Delphi |
GNU (Linux) |
Groovy |
Java |
JavaScript |
.NET |
PCRE (C/C++) |
PCRE2 (C/C++) |
Perl |
PHP |
POSIX |
PowerShell |
Python |
R |
Ruby |
std::regex |
Tcl |
VBScript |
Visual Basic 6 |
wxWidgets |
XML Schema |
Xojo |
XQuery 和 XPath |
XRegExp |
数据库 |
MySQL |
Oracle |
PostgreSQL |
此网站上的更多信息 |
简介 |
正则表达式快速入门 |
正则表达式教程 |
取代字符串教学 |
应用程序和语言 |
正则表达式范例 |
正则表达式参考 |
替换字符串参考 |
使用 std::regex 的 C++ 正则表达式
C++11 标准中定义的 C++ 标准函数库在 <regex>
标头中提供正则表达式的支持。在 C++11 之前,<regex>
是 C++ 标准函数库的 TR1 延伸模块的一部分。当此网站提到 std::regex 时,是指包含在 Visual C++ 2008 及更新版本中的 C++ 标准函数库的 Dinkumware 实作。当目标为 Win64 时,C++Builder XE3 及更新版本也支持此函数库。在 Visual C++ 2008 中,命名空间是 std::tr1::regex,而不是 std::regex。
C++Builder 10 及更新版本支持 Dinkumware 实作 std::regex,只要将使用传统 Borland 编译器的选项禁用,即可锁定 Win32。在 C++Builder XE3 及更新版本中使用传统 Borland 编译器时,您可以使用 boost::regex 取代 std::regex。虽然 std::regex 在 TR1 和 C++11 中定义的运算和类别与 boost::regex 几乎相同,但实际 regex 风格仍有许多重要的差异。最重要的是,Boost 中的 ECMAScript regex 语法添加了许多从 Perl 借用的功能,这些功能并非 ECMAScript 标准的一部分,且未在 Dinkumware 函数库中实作。
六种正则表达式风格
std::regex_constants 中定义了六种不同的正则表达式风格或语法
ECMAScript
:类似于 JavaScriptbasic
:类似于 POSIX BRE。extended
:类似于 POSIX ERE。grep
:与basic
相同,但会将换行符号视为交替操作符。egrep
:与extended
相同,但会将换行符号视为交替操作符。awk
:与extended
相同,但会支持 非可打印字符的常见转义字符。
大多数 C++ 参考都说明 C++11 实作了 ECMA-262v3 和 POSIX 标准中定义的正则表达式。但实际上,C++ 实作是根据这些标准非常松散地创建的。语法非常接近。唯一的重大差异是 std::regex 即使在 ECMAScript 模式下也支持 POSIX 类别,且对于哪些字符必须转义(例如大括号和右中括号)以及哪些字符不需转义(例如字母)有些特别。
但此语法的实际行为有重要的差异。在 std::regex 中,插入符号和美元符号 始终会与内嵌换行符号相符,而在 JavaScript 和 POSIX 中,这是一个选项。与大多数 regex 风格一样,对非参与群组的反向引用无法相符,而在 JavaScript 中,它们会找到零长度相符。在 JavaScript 中,\d
和 \w
仅限于 ASCII,而 \s
则相符于所有 Unicode 空白。这很奇怪,但所有现代浏览器都遵循此规范。在 std::regex 中,使用 char
字符串时,所有 简写 都仅限于 ASCII。在 Visual C++ 中(但 C++Builder 中没有),使用 wchar_t
字符串时,它们支持 Unicode。在 Visual C++ 中使用 wchar_t
时,POSIX 类别 也会相符于非 ASCII 字符,但并未一致包含所有预期的 Unicode 字符。
实际上,您大多会使用 ECMAScript 语法。它是缺省语法,提供的功能远多于其他语法。每当本网站上的教程提到 std::regex 但未提到任何语法时,所写的内容就适用于 ECMAScript 语法,可能适用于其他语法,也可能不适用。您实际上只会在想要重复使用旧 POSIX 代码或 UNIX 脚本中的现有正则表达式时,才会使用其他语法。
创建正则表达式对象
在使用正则表达式之前,您必须创建范本类别 std::basic_regex 的对象。如果您要处理的主题是 char
数组或 std::string 对象,您可以轻松地使用这个范本类别的 std::regex 实例化来运行此动作。如果您要处理的主题是 wchar_t
数组或 std::wstring 对象,请使用 std::wregex 实例化。
将您的正则表达式作为字符串传递给构造函数的第一个参数。如果您想要使用 ECMAScript 以外的正则表达式风格,请将适当的常数作为第二个参数传递。您可以将这个常数「或」运算 std::regex_constants::icase,以让正则表达式不区分大小写。您也可以将它「或」运算 std::regex_constants::nosubs,以将所有捕获组转换为非捕获组,如果您只关心整体正则表达式比对,而且不想要截取任何捕获组比对到的文本,这样可以让您的正则表达式更有效率。
寻找正则表达式比对
调用 std::regex_search(),并将您的主题字符串作为第一个参数,将正则表达式对象作为第二个参数,以检查您的正则表达式是否可以比对字符串的任何部分。如果您想要检查您的正则表达式是否可以比对整个主题字符串,请调用 std::regex_match(),并使用相同的参数。由于 std::regex 缺乏专门在字符串开头和结尾比对的锚定,因此您在使用正则表达式验证用户输入时,必须调用 regex_match()。
regex_search() 和 regex_match() 都只会传回 true 或 false。若要取得 regex_search() 比对到的字符串部分,或是在使用任一函数时取得捕获组比对到的字符串部分,您需要将范本类别 std::match_results 的对象作为第二个参数传递。正则表达式对象接着会成为第三个参数。使用下列四个范本实例化之一的缺省构造函数来创建这个对象
- 如果您要处理的主题是
char
数组,请使用 std::cmatch - 如果您要处理的主题是 std::string 对象,请使用 std::smatch
- 如果您要处理的主题是
wchar_t
数组,请使用 std::wcmatch - 如果您要处理的主题是 std::wstring 对象,请使用 std::wsmatch
当函数调用传回 true 时,您可以调用 match_results 对象的 str()、position() 和 length() 成员函数,以取得配对的文本,或相对于主旨字符串的配对开始位置及其长度。调用这些成员函数时不带参数或以 0 为参数,以取得整体 regex 配对。调用时传递 1 或更大的数字,以取得特定捕获组的配对。size() 成员函数指出捕获组的数量,加上整体配对的 1。因此,您可以传递一个值,范围为 size()-1,至其他三个成员函数。
将所有内容组合在一起,我们可以像这样取得第一个捕获组配对的文本
std::string subject("Name: John Doe"); std::string result; try { std::regex re("Name: (.*)"); std::smatch match; if (std::regex_search(subject, match, re) && match.size() > 1) { result = match.str(1); } else { result = std::string(""); } } catch (std::regex_error& e) { // Syntax error in the regular expression }
寻找所有 Regex 配对
若要寻找字符串中的所有 regex 配对,您需要使用反复运算器。使用这四个范本实例化之一,创建范本类别 std::regex_iterator 的对象
- 当您的主旨是
char
数组时,使用 std::cregex_iterator - 当您的主旨是 std::string 对象时,使用 std::sregex_iterator
- 当您的主旨是
wchar_t
数组时,使用 std::wcregex_iterator - 当您的主旨是 std::wstring 对象时,使用 std::wsregex_iterator
调用构造函数并使用三个参数来创建一个对象:指出搜索开始位置的字符串反复运算器、指出搜索结束位置的字符串反复运算器,以及 regex 对象。如果找到任何配对,对象在创建时将包含第一个配对。使用缺省构造函数创建另一个反复运算器对象,以取得串行结束反复运算器。您可以将第一个对象与第二个对象进行比较,以判断是否有任何进一步的配对。只要第一个对象不等于第二个对象,您就可以取消第一个对象的参考,以取得 match_results 对象。
std::string subject("This is a test"); try { std::regex re("\\w+"); std::sregex_iterator next(subject.begin(), subject.end(), re); std::sregex_iterator end; while (next != end) { std::smatch match = *next; std::cout << match.str() << "\n"; next++; } } catch (std::regex_error& e) { // Syntax error in the regular expression }
取代所有配对
若要取代字符串中的所有配对,请调用 std::regex_replace(),并将您的主旨字符串作为第一个参数、regex 对象作为第二个参数,以及包含取代文本的字符串作为第三个参数。此函数会传回一个套用取代结果的新字符串。
替换字符串的语法与 JavaScript 类似,但并非完全相同。无论您使用哪种 regex 语法或文法,都会使用相同的替换字符串语法。您可以使用 $&
或 $0
来插入整个 regex 比对,并使用 $1
到 $9
来插入前九个捕获组比对到的文本。没有办法插入第 10 个或更高群组比对到的文本。$10 和更高群组永远会被替换为空白,而 $9 和更低群组如果 regex 中的捕获组少于要求的数字,也会被替换为空白。$`
(美元符号反引号)是比对左侧的字符串部分,而 $'
(美元符号单引号)是比对右侧的字符串部分。