C++正则表达式语法

发布于:2025-06-27 ⋅ 阅读:(13) ⋅ 点赞:(0)

在C++中,正则表达式是处理文本模式匹配和字符串操作的强大工具。C++11及以后的标准库提供了<regex>头文件,支持正则表达式的使用。下面是C++正则表达式的核心语法规则和用法:

一、基本正则表达式语法

1. 普通字符

直接匹配自身,例如:a 匹配字符 a

2. 元字符(需转义)

具有特殊含义的字符,需用反斜杠 \ 转义(在C++字符串中需用双反斜杠 \\)。

  • .:匹配除换行符外的任意字符。
  • ^:匹配字符串的开头。
  • $:匹配字符串的结尾。
  • *:匹配前面的元素0次或多次。
  • +:匹配前面的元素1次或多次。
  • ?:匹配前面的元素0次或1次(可选)。
  • {n}:精确匹配n次。
  • {n,}:至少匹配n次。
  • {n,m}:匹配次数在n到m之间(包含n和m)。
3. 字符类
  • [ ]:匹配方括号内的任意一个字符。
    • 例如:[abc] 匹配 abc[0-9] 等价于 \d
  • [^ ]:否定字符类,匹配不在方括号内的任意字符。
    • 例如:[^0-9] 匹配非数字字符。
4. 预定义字符类
  • \d:匹配数字(等价于 [0-9])。
  • \D:匹配非数字(等价于 [^0-9])。
  • \s:匹配空白字符(空格、制表符、换行符等)。
  • \S:匹配非空白字符。
  • \w:匹配单词字符(字母、数字、下划线,等价于 [a-zA-Z0-9_])。
  • \W:匹配非单词字符。
5. 位置锚点
  • ^:匹配字符串的开头。
  • $:匹配字符串的结尾。
  • \b:匹配单词边界(如空格、标点或字符串首尾)。
  • \B:匹配非单词边界。
6. 分组与捕获
  • ( ):定义捕获组,可以提取匹配的子串。
    • 例如:(\\d{4})-(\\d{2})-(\\d{2}) 可捕获日期的年、月、日。
  • (?: ):非捕获组,只用于分组,不捕获匹配结果。

二、C++中的正则表达式类

1. std::regex

用于表示一个正则表达式对象。

#include <regex>
std::regex pattern("\\d+"); // 匹配一个或多个数字
2. 匹配函数
  • std::regex_match:判断整个字符串是否匹配正则表达式。
    std::string str = "123";
    bool match = std::regex_match(str, pattern); // true
    
  • std::regex_search:在字符串中搜索第一个匹配的子串。
    std::string str = "abc123def";
    bool found = std::regex_search(str, pattern); // true
    
  • std::regex_replace:替换匹配的子串。
    std::string result = std::regex_replace(str, pattern, "X"); // "abcXdef"
    
3. 迭代器
  • std::sregex_iterator:遍历所有匹配的子串。
    std::string str = "a1b2c3";
    std::regex pattern("\\d");
    for (std::sregex_iterator it(str.begin(), str.end(), pattern); 
         it != std::sregex_iterator(); ++it) {
        std::cout << it->str() << " "; // 输出: 1 2 3
    }
    

三、正则表达式标志

通过 std::regex 的第二个参数指定匹配模式:

  • std::regex::icase:忽略大小写。
    std::regex pattern("hello", std::regex::icase); // 匹配"Hello"、"HELLO"等
    
  • std::regex::ECMAScript:使用ECMAScript语法(默认)。
  • std::regex::grep:使用POSIX grep语法。

四、捕获组的使用

用圆括号 () 定义捕获组,可以提取匹配的子串。

std::string str = "2023-06-26";
std::regex pattern("(\\d{4})-(\\d{2})-(\\d{2})");
std::smatch match;
if (std::regex_search(str, match, pattern)) {
    std::cout << "Year: " << match[1] << std::endl; // 2023
    std::cout << "Month: " << match[2] << std::endl; // 06
    std::cout << "Day: " << match[3] << std::endl; // 26
}

五、原始字符串字面量(Raw String Literals)

C++11引入的原始字符串语法 R"(...)" 避免了对反斜杠的双重转义,使正则表达式更易读。

// 普通字符串(需双重转义)
std::regex pattern1("\\d{3}-\\d{4}");

// 原始字符串(无需转义)
std::regex pattern2(R"(\d{3}-\d{4})"); // 等价于上面的pattern1

六、示例:验证有效数字的正则表达式

针对LeetCode 65题,可以使用正则表达式验证有效数字:

#include <regex>
#include <string>

class Solution {
public:
    bool isNumber(std::string s) {
        // 使用原始字符串字面量定义正则表达式
        std::regex pattern(
            R"(^[+-]?(\d+\.?|\.\d+)(\d+)?([eE][+-]?\d+)?$)"
        );
        return std::regex_match(s, pattern);
    }
};

七、性能注意事项

  • 正则表达式在处理复杂模式时可能效率较低,尤其是包含回溯或嵌套量词的情况。
  • 对于简单的字符串匹配,手动实现状态机可能比正则表达式更高效。

八、常见错误

  • 忘记转义:在C++字符串中使用元字符时需双反斜杠(如 \\d)。
  • 贪婪匹配*+ 默认是贪婪的,可能匹配尽可能多的字符。使用 *?+? 进行非贪婪匹配。
  • 性能陷阱:避免使用过度复杂的正则表达式,如 .*a.*a.* 在某些情况下会导致灾难性回溯。

通过掌握这些规则,您可以在C++中灵活使用正则表达式处理各种文本匹配问题。

** 静态常量模式的编译优化

  1. 静态常量模式的编译优化
    您的正则表达式版本使用了静态常量成员变量 static const regex pattern:
class Solution{
	// ...
	static const regex pattern;
	// ...
};

const std::regex Solution::pattern(...);

优势:正则表达式只在程序启动时编译一次,后续调用直接使用预编译的模式,避免了重复编译开销。
对比:如果您的 DFA 版本在每次调用时都重新初始化状态转移表(如通过构造函数),会产生额外的内存分配和初始化开销。
2. 正则表达式引擎的优化
现代 C++ 标准库的正则表达式引擎(如 GCC 的 libstdc++ 或 Clang 的 libc++)可能针对简单模式进行了高度优化:
编译时优化:对于固定的正则表达式模式,引擎可能在编译阶段进行部分求值或生成更高效的匹配代码。
DFA/NFA 混合实现:某些正则表达式引擎在处理简单模式时会自动转换为 DFA,从而获得接近手动 DFA 的性能。


网站公告

今日签到

点亮在社区的每一天
去签到