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++ 中内置的正则表达式匹配器包括 regexsmatchsregex_iterator 等。

1. regex

regex 表示一个正则表达式类型,可以用来创建一个正则表达式的对象。我们可以将这个对象作为第二个参数传递给 regex_matchregex_searchregex_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_matchregex_searchregex_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. 常见正则表达式示例

最后,我们提供一些常见的正则表达式示例,以供参考:

以上是一些常见的正则表达式示例,您也可以根据自己的需求编写自己的正则表达式。需要注意的是,正则表达式可能会变得非常复杂,需要谨慎编写和使用。

以下是更多关于正则表达式的相关知识:

9. 标志位

在使用 C++ 的正则表达式库时,可以设置标志位以改变匹配的行为。以下是一些常见的标志位和用法:

例如,以下代码演示了如何使用 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_defaultregex_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 中的增强功能。正则表达式是一种非常方便和强大的字符串处理工具,掌握它并合理编写正则表达式可以提高代码的效率和可读性。