C++ 正则表达式专题
本专题将介绍正则表达式在 C++ 中的应用,包括正则表达式的概念、语法规则、元字符、替换、匹配器、异常、示例和实战等方面的内容。
正则表达式简介
正则表达式是一种用来描述和匹配字符串的规则语言,它可以帮助我们快速准确地搜索和处理字符串数据。在 C++ 中,我们可以使用正则表达式库 regex
来实现正则表达式的功能。
正则表达式可以用来:
- 检测字符串中是否符合某种模式,如验证手机号、邮箱等。
- 在大量文本中快速查找特定模式的单词、语句等。
- 灵活处理文本内容,如按照模式替换、重构文本。
- 对于信息学竞赛来说,正则表达式也是一种非常有用的技术,能够简化数据预处理等操作。
正则表达式通常使用特殊字符和文本字符组成。其中,特殊字符成为元字符,文本字符则表示匹配文本中具有相同字面含义的字符。例如,字符 'a' 对应匹配字符串中正好出现字符 'a' 的位置。
正则表达式语法
正则表达式的语法由特殊字符和文本字符组成,其中特殊字符成为元字符,文本字符则表示匹配文本中具有相同字面含义的字符。正则表达式在 C++ 中由一系列字符和标记组成,如下所示:
regex pattern;
其中 pattern
表示正则表达式的模式,可以是字符串常量或字符串变量。正则表达式的语法规则如下:
1. 字符集
在正则表达式中,方括号 "[ ]" 表示一个字符集,其中包含多个元字符。字符集可以匹配一个任意字符,可以使用单横线 "-" 表示区间,如 "[0-9]" 表示匹配任意一个数字符号。除了特殊字符 (如 ".", "^", "$" 以及 "" 等) 外,字符集中的元字符均失效。
例如,以下正则表达式匹配任意一个小写字母:
regex pattern("[a-z]");
在这个正则表达式中,"[a-z]" 代表一个字符集,其中包括从 'a' 到 'z' 的所有小写字母。
2. 重复
在正则表达式中,""、"+" 和 "?" 都表示重复。"" 表示匹配前面的元素零次或多次,"+" 表示匹配前面的元素一次或多次,"?" 表示匹配前面的元素零次或一次。
例如,以下正则表达式匹配任意一个数字:
regex pattern("[0-9]+");
在这个正则表达式中,"[0-9]+" 代表一个连续的数字串,这个数字串中的数字可以重复多次。
3. 位置
在正则表达式中,"^" 和 "$" 分别表示字符串的开始和结束位置。
例如,以下正则表达式匹配字符串是否以字符 'a' 开头:
regex pattern("^a");
在这个正则表达式中,"^a" 代表字符串必须以字符 'a' 开头。
4. 分组
在正则表达式中,圆括号 "( )" 表示一个分组,其中包含多个元字符。分组可以使元字符作为一个整体进行匹配。
例如,以下正则表达式匹配字符串内是否包含单词 "the":
regex pattern("\\b(the)\\b");
在这个正则表达式中,"\b" 代表一个单词分界符,以确保 "the" 被匹配为一个单独的单词。
正则表达式元字符
正则表达式中的元字符是指具有特殊含义的字符,它们具有不同的作用和语法规则。在正则表达式中,元字符主要有以下几种:
1. "." 元字符
在正则表达式中,点号 "." 表示任意单个字符,可以用于匹配除换行字符外的任意字符。
例如,以下正则表达式匹配任意一个以字符 'a' 开头且以字符 'z' 结尾的字符串:
regex pattern("a.*z");
在这个正则表达式中,".*" 代表除换行字符外的任意字符均可匹配,可以用于匹配字符 'a' 和字符 'z' 之间的任意字符。
2. "^" 元字符
在正则表达式中,"^" 表示开始位置,用于匹配字符串的起始位置。
例如,以下正则表达式匹配任意一个以字符 'a' 开头的字符串:
regex pattern("^a.*");
在这个正则表达式中,"^a" 代表字符串必须以字符 'a' 开头。
3. "$" 元字符
在正则表达式中,"$" 表示结束位置,用于匹配字符串的结束位置。
例如,以下正则表达式匹配任意一个以字符 'z' 结尾的字符串:
regex pattern(".*z$");
在这个正则表达式中,"z$" 代表字符串必须以字符 'z' 结尾。
4. "*"、 "+" 和 "?" 元字符
在正则表达式中,"*"、 "+" 和 "?" 分别表示重复 0 次、1 次或 0 次到 1 次。
例如,以下正则表达式匹配任意一个大小写字母:
regex pattern("[a-zA-Z]*");
在这个正则表达式中,"[a-zA-Z]*" 代表大小写字母可重复出现多次,包括空字符。
5. "{n}"、"{n, }" 和 "{n,m}" 元字符
在正则表达式中,"{n}"、"{n, }" 和 "{n,m}" 分别表示重复 n 次、n 次到最大值、n 次到最小值到 m 次。
例如,以下正则表达式匹配任意一个至少 3 位数的数字串:
regex pattern("\\d{3,}");
在这个正则表达式中,"\d{3,}" 代表数字至少出现 3 次,且没有上限。
6. "[ ]" 元字符
在正则表达式中,"[ ]" 表示一个字符集。字符集内包括多个元字符,用于匹配任意一个字符。
例如,以下正则表达式匹配任意一个大小写字母:
regex pattern("[a-zA-Z]");
在这个正则表达式中,"[a-zA-Z]" 代表大小写字母其中之一。
7. "[^fn1]" 元字符
在正则表达式中,"[^fn1]" 表示一个反向字符集。反向字符集内包含用于匹配不属于该集合的任意一个字符。
例如,以下正则表达式匹配任意一个非空字符:
regex pattern("[^fn2]");
在这个正则表达式中,"\s" 代表一个空字符,"[^fn3]" 代表非空字符。
8. "|" 元字符
在正则表达式中,"|" 表示 "或"。可以用于在多个正则表达式之间选择使用其中任意一个。
例如,以下正则表达式匹配任意一个以字符 'a' 或字符 'b' 开头的字符串:
regex pattern("^(a|b).*");
在这个正则表达式中,"(a|b)" 代表以字符 'a' 或字符 'b' 开头。
9. "" 元字符
在正则表达式中,"" 用来转义元字符本身。任何以 "" 开头的字符串都可以看作一个转义字符。
例如,以下正则表达式匹配 "" 字符本身:
regex pattern("\\\\");
在这个正则表达式中,"\" 代表 "" 字符本身。
正则表达式替换
在 C++ 的正则表达式中,我们可以使用 regex_replace
函数来实现替换的功能。这个函数接受三个参数:
string regex_replace(const string& subject, const regex& pattern, const string& replacement);
其中,第一个参数 subject
表示待处理的字符串,第二个参数 pattern
表示正则表达式的模式,第三个参数 replacement
则表示替换的字符串。
例如,以下代码演示了如何使用正则表达式来替换字符串中的特定部分:
#include <regex>
#include <iostream>
using namespace std;
int main() {
string str = "The quick brown fox jumped over the lazy dog.";
regex pattern("\\bquick\\b");
string replacement = "slow";
string result = regex_replace(str, pattern, replacement);
cout << result << endl;
return 0;
}
在这个代码中,我们使用正则表达式 "\bquick\b" 匹配字符串中的单词 "quick",然后将其替换为单词 "slow"。最终的输出结果为:The slow brown fox jumped over the lazy dog.。
正则表达式匹配器
在 C++ 中,我们可以使用正则表达式匹配器来实现正则表达式的匹配功能。C++ 中内置的正则表达式匹配器包括 regex
、smatch
和 sregex_iterator
等。
1. regex
regex
表示一个正则表达式类型,可以用来创建一个正则表达式的对象。我们可以将这个对象作为第二个参数传递给 regex_match
、regex_search
和 regex_replace
等函数,并使用它们来实现正则表达式的匹配功能。
例如,以下代码演示了如何使用 regex
对象来匹配字符串:
#include <regex>
#include <iostream>
using namespace std;
int main() {
string str = "The quick brown fox.";
regex pattern("\\bquick\\b");
bool match = regex_match(str, pattern);
cout << (match ? "Matched." : "Not matched.") << endl;
return 0;
}
在这个代码中,我们使用正则表达式 "\bquick\b" 匹配字符串中的单词 "quick",然后通过 regex_match
函数判断匹配结果。如果匹配成功,则输出 "Matched.";否则输出 "Not matched."。
2. smatch
smatch
对象表示一个正则表达式的匹配结果。我们可以使用 regex_match
、regex_search
和 regex_replace
等函数来获取匹配结果,并将保存结果的 sratch
对象作为第二个参数传递给这些函数。
例如,以下代码演示了如何使用 smatch
对象来输出正则表达式的匹配结果:
#include <regex>
#include <iostream>
using namespace std;
int main() {
string str = "The quick brown fox.";
regex pattern("\\b(\\w+)\\b");
smatch match;
if (regex_search(str, match, pattern)) {
cout << "Matched! Submatches:" << endl;
for (size_t i = 0; i < match.size(); ++i) {
ssub_match submatch = match[i];
string submatch_str = submatch.str();
cout << " " << i << ": " << submatch_str << endl;
}
} else {
cout << "Not matched." << endl;
}
return 0;
}
在这个代码中,我们使用正则表达式 "\\b(\\w+)\\b" 匹配字符串中的单词,并将保存结果的 `smatch` 对象 `match` 作为第二个参数传递给 `regex_search` 函数。如果匹配成功,则输出每一个匹配到的子串,并通过 `ssub_match::str` 函数获取每个子串的字符串形式。输出结果如下:
Matched! Submatches: 0: quick 1: quick
### 3. `sregex_iterator`
`sregex_iterator` 对象表示一个正则表达式匹配结果的迭代器。我们可以使用 `regex_match`、`regex_search` 和 `regex_replace` 等函数来获取匹配结果,并通过 `sregex_iterator` 对象遍历匹配结果。
例如,以下代码演示了如何使用 `sregex_iterator` 对象来获取正则表达式的多个匹配结果:
```cpp
#include <regex>
#include <iostream>
using namespace std;
int main() {
string str = "The quick brown fox.";
regex pattern("\\b(\\w+)\\b");
sregex_iterator it(str.begin(), str.end(), pattern);
sregex_iterator end_it;
while (it != end_it) {
smatch match = *it;
cout << "Matched! Submatches:" << endl;
for (size_t i = 0; i < match.size(); ++i) {
ssub_match submatch = match[i];
string submatch_str = submatch.str();
cout << " " << i << ": " << submatch_str << endl;
}
++it;
}
return 0;
}
在这个代码中,我们使用正则表达式 "\b(\w+)\b" 匹配字符串中的单词,并创建一个 sregex_iterator
对象 it
,它遍历输入字符串的所有匹配结果。end_it
是一个 sregex_iterator
对象,表示遍历结束的迭代器。
接下来,在一个 while 循环中,我们不断地获取 it
所指向的 smatch
对象,并输出其中的每个子串。输出结果如下:
Matched! Submatches:
0: The
Matched! Submatches:
0: quick
1: quick
Matched! Submatches:
0: brown
Matched! Submatches:
0: fox
需要注意的是,由于我们的正则表达式 "\b(\w+)\b" 中包含一个捕获组,因此每个匹配结果都有两个子串,包括整个字符串和捕获组匹配的子串。第一个子串的索引是0,表示整个匹配结果,而第二个子串的索引是1,表示捕获组匹配的子串。
4. regex_replace
regex_replace
函数可以用来在输入字符串中查找并替换子串。它有两个版本:一个版本接受正则表达式对象和替换字符串作为参数,另一个版本接受一个函数对象作为替换器。
以下代码演示如何使用 regex_replace
函数进行字符串的替换:
#include <regex>
#include <string>
#include <iostream>
using namespace std;
int main() {
string str = "The quick brown fox.";
regex pattern("\\b(\\w+)\\b");
string replacement = "<$1>";
string result = regex_replace(str, pattern, replacement);
cout << "Original string: " << str << endl;
cout << "Result string: " << result << endl;
return 0;
}
在这个代码中,我们使用正则表达式 "\b(\w+)\b" 匹配字符串中的单词,并将匹配结果替换成带有尖括号的形式。具体来说,我们将替换字符串设置为 "<$1>",其中 "$1" 表示正则表达式的第一个捕获组,即匹配到的单词。最后,我们将输入字符串和正则表达式传递给 regex_replace
函数,并将替换结果保存在 result
变量中。输出结果如下:
Original string: The quick brown fox.
Result string: The <quick> <brown> <fox>.
5. 自定义替换函数
在某些情况下,我们可能需要使用自定义函数来决定替换的结果,而不是简单地使用固定字符串。在这种情况下,我们可以使用 regex_replace
函数的第三个参数,即替换函数。
以下代码演示了如何使用自定义替换函数来进行字符串的替换:
#include <regex>
#include <string>
#include <iostream>
using namespace std;
string to_uppercase(const smatch& match) {
string result = match.str();
transform(result.begin(), result.end(), result.begin(), toupper);
return result;
}
int main() {
string str = "The quick brown fox.";
regex pattern("\\b(\\w+)\\b");
string result = regex_replace(str, pattern, to_uppercase);
cout << "Original string: " << str << endl;
cout << "Result string: " << result << endl;
return 0;
}
在这个代码中,我们定义了一个自定义函数 to_uppercase
,它接受一个 smatch
对象作为参数,并返回一个字符串。该函数将匹配到的子串转换成大写字母,并返回结果。
接下来,在主函数中,我们使用正则表达式 "\b(\w+)\b" 匹配字符串中的单词,并将每个单词替换成大写字母形式。具体来说,我们将替换函数设置为 to_uppercase
,该函数接受一个 smatch
对象作为参数,并返回要替换成的字符串。最后,我们将输入字符串和正则表达式传递给 regex_replace
函数,并将替换结果保存在 result
变量中。输出结果如下:
Original string: The quick brown fox.
Result string: THE QUICK BROWN FOX.
需要注意的是,在自定义替换函数中,我们可以通过 smatch::str
函数获取匹配到的子串的字符串形式,并在函数内部进行任何操作。
6. regex_search
regex_search
函数可以用来在输入字符串中查找满足正则表达式的子串。该函数返回一个 bool
类型值,表示是否发现匹配结果。
以下代码演示如何使用 regex_search
函数进行字符串的查找:
#include <regex>
#include <string>
#include <iostream>
using namespace std;
int main() {
string str = "The quick brown fox.";
regex pattern("\\b\\w{5}\\b");
bool found = regex_search(str, pattern);
cout << "Original string: " << str << endl;
if (found) {
cout << "String contains a 5-letter word." << endl;
} else {
cout << "String does not contain a 5-letter word." << endl;
}
return 0;
}
在这个代码中,我们使用正则表达式 "\b\w{5}\b" 匹配字符串中的五个字符的单词,并返回查找结果。具体来说,我们将正则表达式设置为 "\b\w{5}\b",其中 "\w{5}" 表示匹配五个字符的单词。
最后,我们将输入字符串和正则表达式传递给 regex_search
函数,然后根据返回值输出查找结果。输出结果如下:
Original string: The quick brown fox.
String contains a 5-letter word.
7. regex_match
regex_match
函数可以用来在输入字符串的开头查找满足正则表达式的子串。该函数返回一个 bool
类型值,表示是否发现匹配结果。
以下代码演示如何使用 regex_match
函数进行字符串的查找:
#include <regex>
#include <string>
#include <iostream>
using namespace std;
int main() {
string str = "The quick brown fox.";
regex pattern("\\bThe\\b");
bool matched = regex_match(str, pattern);
cout << "Original string: " << str << endl;
if (matched) {
cout << "String starts with 'The'." << endl;
} else {
cout << "String does not start with 'The'." << endl;
}
return 0;
}
在这个代码中,我们使用正则表达式 "\bThe\b" 匹配字符串开头的单词 "The",并返回匹配结果。具体来说,我们将正则表达式设置为 "\bThe\b",其中 "\b" 表示单词边界。
最后,我们将输入字符串和正则表达式传递给 regex_match
函数,然后根据返回值输出匹配结果。输出结果如下:
Original string: The quick brown fox.
String starts with 'The'.
8. 常见正则表达式示例
最后,我们提供一些常见的正则表达式示例,以供参考:
- 匹配整数:
-?[0-9]+
- 匹配浮点数:
-?[0-9]+(\.[0-9]+)?
- 匹配日期:
([0-9]{4})-([0-9]{2})-([0-9]{2})
- 匹配电子邮件地址:
[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
- 匹配 URL:
((http[s]?|ftp):\/\/)?([^fn4]+)((\/\w+)*\/)([\w\-\.]+[^fn5]+)(.*)?(#[\w\-]+)?
- 匹配用户名:
[a-zA-Z0-9_]{4,16}
- 匹配密码:
(?!^[0-9]+$)(?!^[a-zA-Z]+$)(?!^[^fn6]+$).{6,16}
以上是一些常见的正则表达式示例,您也可以根据自己的需求编写自己的正则表达式。需要注意的是,正则表达式可能会变得非常复杂,需要谨慎编写和使用。
以下是更多关于正则表达式的相关知识:
9. 标志位
在使用 C++ 的正则表达式库时,可以设置标志位以改变匹配的行为。以下是一些常见的标志位和用法:
regex_constants::icase
:忽略大小写,比如判断匹配字符串时不区分大小写。regex_constants::ECMAScript
:使用 ECMAScript 的正则表达式语法。regex_constants::extended
:支持扩展规则,比如允许在正则表达式中添加注释。regex_constants::multiline
:使用多行模式,比如^
和$
匹配每一行的开头和结尾。regex_constants::optimize
:对正则表达式进行优化,提高匹配速度。regex_constants::grep
:模拟 grep 命令的行为。
例如,以下代码演示了如何使用 regex_constants::icase
标志位忽略大小写进行匹配:
#include <regex>
#include <string>
#include <iostream>
using namespace std;
int main() {
string str = "The quick Brown fox.";
regex pattern("brown", regex_constants::icase);
bool found = regex_search(str, pattern);
cout << "Original string: " << str << endl;
if (found) {
cout << "String contains 'brown' (case-insensitive)." << endl;
} else {
cout << "String does not contain 'brown' (case-insensitive)." << endl;
}
return 0;
}
在这个代码中,我们将正则表达式库调用时的第二个参数设置为 regex_constants::icase
,表示使用不区分大小写的匹配。最后输出的结果如下:
Original string: The quick Brown fox.
String contains 'brown' (case-insensitive).
10. C++17 中的正则表达式增强
C++17 对正则表达式进行了增强,使得编写和使用正则表达式更加方便和高效。以下是一些 C++17 中的正则表达式增强:
1. string_view
支持
C++17 中,正则表达式库已经支持 string_view
,不必再进行字符串拷贝。可以使用以下方式进行匹配:
#include <regex>
#include <string_view>
#include <iostream>
using namespace std;
int main() {
string_view str = "The quick brown fox.";
regex pattern("\\b\\w{5}\\b");
bool found = regex_search(str, pattern);
cout << "Original string: " << str << endl;
if (found) {
cout << "String contains a 5-letter word." << endl;
} else {
cout << "String does not contain a 5-letter word." << endl;
}
return 0;
}
2. 延迟匹配
C++17 中,正则表达式库允许使用 regex_constants::match_flag_type::match_default
标志位进行延迟匹配,只有在需要时才进行匹配,提高了匹配效率。
例如以下代码演示了延迟匹配的使用:
#include <regex>
#include <string>
#include <iostream>
using namespace std;
int main() {
string str = "The quick brown fox.";
regex pattern("\\b\\w{5}\\b", regex_constants::match_flag_type::match_default | regex_constants::match_flag_type::not_null);
sregex_iterator it(str.begin(), str.end(), pattern);
sregex_iterator end;
cout << "Original string: " << str << endl;
while (it != end) {
smatch match = *it;
cout << "Matched word: " << match.str() << " at position " << match.position() << endl;
++it;
}
return 0;
}
在这个代码中,我们将 regex_constants::match_flag_type::match_default
和 regex_constants::match_flag_type::not_null
两个标志位传递给正则表达式库来使用延迟匹配。最后输出的结果如下:
Original string: The quick brown fox.
Matched word: quick at position 4
Matched word: brown at position 10
3. smatch
实现匹配结果
C++17 中,新引入了一个 smatch
类型,用于保存正则表达式匹配的结果。smatch
类型可以直接调用 size()
函数获取匹配结果的数量,并通过 operator[]
操作符访问对应的匹配结果。
例如以下代码演示了如何使用 smatch
类型来访问正则表达式的匹配结果:
#include <regex>
#include <string>
#include <iostream>
using namespac std;
int main() {
string str = "The quick brown fox.";
regex pattern("\\b\\w{5}\\b");
smatch matches;
regex_search(str, matches, pattern);
cout << "Original string: " << str << endl;
cout << "Number of matches: " << matches.size() << endl;
cout << "The first match: " << matches[0].str() << " at position " << matches[0].position() << endl;
return 0;
}
在这个代码中,我们使用 smatch
类型来保存正则表达式的匹配结果。最后输出的结果如下:
Original string: The quick brown fox.
Number of matches: 2
The first match: quick at position 4
11. 总结
本文介绍了 C++ 中正则表达式的基本用法,包括正则表达式对象的创建,正则表达式的匹配和查找,以及正则表达式的常见用法和 C++17 中的增强功能。正则表达式是一种非常方便和强大的字符串处理工具,掌握它并合理编写正则表达式可以提高代码的效率和可读性。