1. 概述
1.1 正则表达式概述
正则表达式(Regular Expressions,简称 regex)是用于匹配文本模式的一种特殊字符序列,其可以用一系列字符来表示出不同文本的对应模式。正则表达式的应用范围十分广泛,包括验证文本格式、判断字符种类、解析文本信息、转换目标文本、遍历搜索文本、符号化文本等。
一般在文件搜索、浏览器搜索时都可以使用正则表达式来表达某一种想要的文本格式,在C++等编程语言中也是一样,灵活正当地使用正则表达式可以很有效地提高代码的可读性和简洁性。
另外要注意的是,随着时间的推移,正则表达式的语法也在扩充升级,而C++对以下几种语法均有着很好的支持,其中最主要的掌握ECMAScript就好:
- ECMAScript:基于ECMAScript标准的语法。JavaScript、ActionScript和Jscript等语言都使用该标准。
- basic:基本的POSIX语法。
- extended:拓展的POSIX语法。
- awk:POSIX awk实用工具使用的语法。
- grep:POSIX grep实用工具使用的语法。
- egrep:用逗号分隔的POSIX egrep语法。
1.2 C++中使用正则表达式
在 C++11 之前,C++ 并未内置正则表达式库,不能直接使用正则表达式。如果开发者想要使用正则表达式对文本作快捷操作,那么就需要依赖第三方库如 PCRE (Perl Compatible Regular Expressions) 或使用 Boost库中的 Boost.Regex。而在 C++11 中,C++ 标准库引入了<regex>
头文件,这样在C++中使用正则表达式就不需要链接导入第三方库了。
示例:
#include <iostream>
#include <regex>
using namespace std;
int main(){
string text = "The number is 42";
regex number_pattern("\\d+"); // 建立匹配模式,查找字符串中是否有数字
if(regex_search(text, number_pattern)){
cout << "Found a number!" << endl;
}
}
2. 正则表达式的语法规则
正则表达式会用到的特殊字符为:^ $ \ . * + ? () [] {} |
每个字符的含义和用处将在下面逐步介绍。
2.1 单字符匹配
除了正则表达式会用到的特殊字符外,单个字符(包括大小写字母、数字、其他字符等)就匹配指定的字符,比如:a
匹配字符 a
。
如果要匹配特殊字符,则需要使用特殊字符中的转义字符\
,比如:\^
匹配字符 ^
对于需要匹配的文本中有未指定字符,就可以使用通配符.
(点,英文句号)。通配符可以匹配任意单个字符,一般用在有部分不确定字符的情况下,比如a.c
可以匹配到abc
、a1c
等一系列文本。只使用单个通配符就表示只匹配总长度为1的文本。
如果指定匹配任意数字或任意字母等非全部的情况时,可以使用字符类来指定一个范围的字符,见下一小节。
2.2 字符类
-
预定义字符类:
是正则表达式内定的用于表示一类字符的情况,用于匹配单一字符
\\d
:匹配任何数字字符(0-9)。\\D
:匹配任何非数字字符。\\w
:匹配任何字母、数字或下划线(A-Z、a-z、0-9 和_
)。\\W
:匹配任何非字母、数字或下划线的字符。\\s
:匹配任意空白字符(如空格、制表符、换行符等)。\\S
:匹配任何非空白字符。
-
自定义字符集:
正则表达式支持使用
[]
来自定义字符集,用于匹配单一字符,比如:[abc]
匹配字符a
、b
或c
。使用
-
来指定字典顺序的前后范围,比如:[0-9]
匹配任意数字;[a-z]
匹配任意小写字母;[A-Za-z]
匹配任意字母
2.3 重复字符匹配
在正则表达式中,特殊字符*
、+
、?
被称为量词,用于匹配某个字符是否多次重复出现的情况。
-
*
(星号):表示出现零次或多次。用于匹配此符号前一个字符零次或多次,比如:
a*
可以匹配到空字符串
、a
、aa
、aaa
等。 -
+
(加号):表示出现一次或多次。用于匹配此符号前一个字符一次或多次,比如:
a+
可以匹配到a
、aa
、aaa
等,但不会匹配空字符串。 -
?
(问号):表示出现零次或一次。用于匹配此符号前一个字符零次或一次,比如:
a?
可以匹配到空字符串
或者a
。
除此之外,还可以使用{}
来指定出现次数的方位,格式为{min, max}
,即匹配此符号前一个字符至少min
次,至多max
次,可省略其中的,
和后一个max
数值:
-
{n}
:用于匹配此符号前一个字符正好n次,比如:
a{3}
只能匹配到aaa
。 -
{min, }
:用于匹配此符号前一个字符至少min次,比如:
a{3, }
可以匹配到aaa
、aaaa
、aaaaa
等。 -
{min, max}
:用于匹配此符号前一个字符至少min次,至多max次,比如:
a{3, 5}
只能匹配到aaa
、aaaa
、aaaaa
。
2.4 边界匹配
在正则表达式中,可以单独指定匹配文本的开头或结尾。分别使用锚点^
来指定开头,使用锚点$
来指定结尾
-
^
:将这个符号放在文本首位,表明文本在此开头。用于匹配字符串的开头,比如:正则表达式
^abc
匹配以abc开头的任意文本abc1
、abcd
等 -
$
:将这个符号放在文本末尾,表明文本在此结尾。用于匹配字符串的结尾,比如:正则表达式
abc$
匹配以abc结尾的任意文本1abc
、dabc
等
锚点还可以混合使用,同时指定文本的开头和结尾,比如:^abc$
将只匹配字符串abc
。还可以更复杂的混合使用,比如:^abc.*123$
就可以匹配以abc开头和123结尾的任意文本了。
锚点除了这两个最常用的,还有很多指定更多特殊情况的,比如以下四种:
-
\b
:用于匹配文本中指定的字母字符串(单词)有边界的情况。有
\b
代表必须有边界,边界是指无任意字符,当然可以是空格。比如:正则表达式
\babc
会匹配abc前面有边界的情况,如xyz abc
(abc前面有空格作边界,无其他字符相连)、abcdef
(abc前面无其他字符相连,后面不用管),而不能匹配abclabc
;正则表达式\babc\b
会匹配abc前后有边界的情况,如abc
、xyz abc nnn
,但不会匹配111abc
或abcddd
. -
\B
:与
\b
的情况相反,用于匹配文本中指定的字母字符串(单词)无边界的情况。比如:正则表达式\Babc
会匹配xabc
(abc前面无边界) ,但不会匹配abc
。 -
\A
:用于匹配字符串的绝对开头,与
^
的行为相似,但是^
在多行模式下可能匹配每行的开头,而\A
始终匹配整个字符串的开头。比如:\Aabc
只会匹配以 "abc" 开头的字符串。 -
\Z
:用于匹配字符串的绝对结尾,与
$
的行为相似,但是$
在多行模式下可能匹配每行的结尾,而\Z
始终匹配整个字符串的结尾。比如:\Z123
只会匹配以123
结尾的字符串。
2.5 分组和选择
正则表达式通过 ()
可以将多个字符组合为一个组,可以对一个组的字符进行统一操作,比如:(ab)+
可以匹配 ab
、abab
、ababab
等。
正则表达式使用 |
表示选择,可以理解为逻辑中的或,比如: (a|b)
表示匹配字符 a
或 b
。
3. C++<regex>
标准库的基本使用
<regex>
类是C++11引入的标准库,需要包含头文件#include <regex>
,并使用命名空间std
。
官方中文文档点这里传送。
3.1 常用类和方法
std::regex
: 表示一个正则表达式。它提供了存储和操作正则表达式的能力。std::smatch
: 用于存储std::regex_match
函数的结果。这是一个用于匹配字符串的结果类型,通常使用于std::string
。std::cmatch
: 与std::smatch
类似,但用于处理 C 风格字符串(C-strings)。std::regex_search
: 用于在字符串中搜索与正则表达式匹配的部分。std::regex_match
: 用于检查一个字符串是否完全匹配给定的正则表达式。std::regex_replace
: 用于替换字符串中与正则表达式匹配的部分。
3.2 创建正则表达式
使用std::regex
类来声明一个正则表达式的匹配模式
示例:
/// 声明一个正则表达式,用于之后匹配一个或多个小写字母
std::regex pattern("[a-z]+");
3.3 匹配
使用std::regex_match()
来检查整个字符串是否完全匹配正则表达式。
第一个参数为目标字符串,第二个参数为指定正则表达式。匹配返回true,否则返回false。
示例:
std::string str = "hello";
std::regex pattern("hello");
if (std::regex_match(str, pattern)) {
std::cout << "完整匹配成功!" << std::endl;
} else {
std::cout << "不匹配。" << std::endl;
}
使用std::regex_search()
查找字符串中是否存在与模式匹配的子字符串。
第一个参数为目标字符串,第二个参数为指定正则表达式。匹配返回true,否则返回false。
std::string str = "hello world";
std::regex pattern("world");
if (std::regex_search(str, pattern)) {
std::cout << "找到匹配的部分!" << std::endl;
} else {
std::cout << "未找到匹配。" << std::endl;
}
3.4 捕获组
在正则表达式中用括号()
包围一段模式来捕获文本中符合格式的子字符串,被称为捕获组。当正则表达式包含捕获组时,可以通过 std::smatch
或 std::cmatch
来保存匹配的结果。且要注意在声明正则表达式时使用关键字R
来激活捕获组。
std::smatch
或 std::cmatch
对象放在std::regex_match()
第二个参数。
示例:
std::string str = "2023-10-08";
std::regex pattern(R"(\d{4})-(\d{2})-(\d{2})"); // 分别捕获年、月、日格式
/// 创建 smatch 对象以保存匹配结果
std::smatch match;
if (std::regex_match(str, match, pattern)) {
std::cout << "年: " << match[1] << ", 月: " << match[2] << ", 日: " << match[3] << std::endl;
}
3.5 替换
使用 std::regex_replace()
替换匹配部分。
第一个参数为目标字符串,第二个参数为指定正则表达式,第三个参数为替换的字符串。替换成功后返回替换后的字符串,否则返回原字符串。
示例:
std::string str = "I love cats and cats are great.";
std::regex pattern("cats");
std::string replaced = std::regex_replace(str, pattern, "dogs");
std::cout << "替换后的字符串: " << replaced << std::endl;
3.6 指定正则选项
<regex>
允许在创建 std::regex
对象时,通过第二个参数指定一些正则选项,使用std::regex_constants
命名空间中的常量来指定选项。
-
std::regex_constants::icase
:忽略大小写在匹配时,忽略字符的大小写。
-
std::regex_constants::multiline
:多行匹配在多行字符串中,
^
和$
匹配每行的开始和结束,而不仅仅是整段字符串的开始和结束。 -
std::regex_constants::dotall
:点号匹配换行符.
匹配的任意字符中增加换行符。 -
std::regex_constants::ECMAScript
:使用 ECMAScript 语法 -
std::regex_constants::basic
:使用基本的 POSIX 语法 -
std::regex_constants::extended
:使用扩展的 POSIX 语法
注意:可以使用|
来组合多个选项
示例
/// 忽略大小写
std::regex pattern("hello", std::regex_constants::icase);
/// 使用基本的 POSIX 语法
std::regex pattern("hello\\s\\(world\\)", std::regex_constants::basic);
/// 忽略大小写并且支持多行匹配
std::regex pattern("hello", std::regex_constants::icase | std::regex_constants::multiline);