文章目录
前言
从C++11开始,C++app开发定制公司开始支持正则表达式的使用,app开发定制公司用于匹配字符串时非常方便!(app开发定制公司比如从爬取的网页源码app开发定制公司中提取指定的字符串、app开发定制公司匹配用户输入邮箱是否app开发定制公司为正确格式、app开发定制公司替换一篇文章中指定的app开发定制公司所有字符串)
app开发定制公司但正如大家所看到的,C++app开发定制公司一如既往的使用模板实现,app开发定制公司以至于我们使用的时候,如果出错了,很难看明白到底哪里出错了
所以本文对C++正则表达式库进行详细解析,便于大家的使用!
一、总体观摩
正则表达式库为regex,使用C++11及以上即可正常使用
#include<regex>
- 1
1.纵观所有类
regex文件里总共有7个模板类以及若干实例化的类
模板类 | 实例化类 | 用途 |
---|---|---|
basic_regex | regex:实际为basic_regex<char>,wregex:实际为basic_regex<wchar_t>,下面的类似,不再一一详细说明 | 作为正则表达式对象,用于匹配文本 |
match_results | cmatch,wcmatch,smatch,wsmatch | 用于获得匹配到的结果,实际可以看作sub_match的数组 |
sub_match | csub_match,wcsub_match ,ssub_match ,wssub_match | 保存捕获组,一般直接用match_result数组访问的方式直接调用,所以一般看不到它 |
regex_iterator | cregex_iterator,wcregex_iterator,sregex_iterator,wsregex_iterator | 用于遍历结果或子匹配的迭代器 |
regex_token_iterator | cregex_token_iterator,wcregex_token_iterator,sregex_token_iterator,wsregex_token_iterator | 用于遍历未匹配部分的迭代器 |
regex_error | 无 | 报告正则表达式库生成的错误 |
regex_traits | 无 | 描述用于匹配的元素的特征。一般用不上,有需求的可参考官方文档 |
虽然看着挺多,但其实平时真正用到的只有少数几个
特别需要注意的是,上面的类都是分类别配对使用的,比如:
regex ,cmatch,csub_match,cregex_iterator
- 1
就是一组
或者
regex ,smatch,ssub_match,sregex_iterator
- 1
也是一组
可以总结出的规律有:
- 前缀没有w字母的为操作多字节字符,添加了w的则为操作宽字节字符
- 前缀有c的,代表是操作char*类型字符串
- 前缀有s的,代表是操作string类型字符串
注意,char*类型字符串与string类型实现类之间不能混用,否则会出错!
2.纵观所有函数
函数 | 用途 |
---|---|
regex_match | 匹配指定字符串整体是否符合 |
regex_search | 匹配字符串中符合的子字符串 |
regex_replace | 替换字符串中指定的字符串 |
二、使用详解
如何写正则表达式就不做细说,网上有很多教程,只对函数如何使用作出详细介绍
1.使用前需要了解的东西
下图为regex库默认使用的ECMAScript文法的表达式
如果想要更改为其它文法,只需要在regex构造函数中最后一位填入对应文法即可,例如:
regex r("<.*?>(.*)<.*?>", regex_constants::grep);
- 1
当然除了选择文法,还可以选择其它标志,只需要将他们用符号 | 连接起来即可
如忽略大小写匹配可以写为
regex r("<.*?>(.*)<.*?>", regex_constants::grep|regex_constants::icase);
- 1
可以看到,其实这些可选项都在regex_constants中,还有其它可选项如下:
不同文法之间的差异
还需要注意的是,C++中许多字符需要添加\ 符号进行转义才能使用,过于麻烦,所以C++11之后,出现了如下写法:
R"dem(内容)dem"
- 1
使用该写法就可以不再转义即可使用,其中dem为任意字符,但要求前后一致即可,其它为固定写法
2.测试字符串
该功能用到regex_match 函数
一般来说,最常用的就是下面这种写法:
比如测试用户输入字符串是否包含@符号:
regex r(".*@.*");string str;cin >> str;if (regex_match(str, r)) { cout << "匹配成功";}else { cout << "匹配失败";}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
3.搜索子字符串
该功能用到regex_search函数
一般用法肯定是找出一段文本中所需要的子字符串,用法如下
string sStr; //要进行匹配的字符串 std::string::const_iterator begin = sStr.begin(); //开始迭代器 std::string::const_iterator end = sStr.end(); //结束迭代器 std::smatch m; //匹配的结果 regex r; //正则表达式 while (std::regex_search(begin, end, m, r)) { begin = m[0].second; //更新开始迭代器的位置 m[n].str(); //获得第n个捕获组,其中0表示匹配到的全部子字符串 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
该代码段就是不断从sStr中匹配符合r的子字符串,匹配成功则返回true,并将匹配到的结果放在m中,可通过m[n].str()方式返回指定捕获组的子字符串
同时m[0].second记录了当前匹配到的位置,所以通过它更新begin ,就可以遍历所有子字符串,直到无法匹配,返回false,结束
4.使用迭代器
如果你认为regex_search用起来比较麻烦,则可以使用迭代器,用法如下:
regex r("-(.*?)-"); string s = "yushi-csdn--yushi-csdn"; sregex_iterator beg(s.begin(),s.end(),r); sregex_iterator end; for (; beg != end; beg++) { cout << beg->str(1) << endl; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
由于sregex_iterator 默认构造函数为指向最后一个元素之后,所以对end没有进行任何处理,只是作为一个结束标志
成员函数str可以返回指定捕获组的字符串,不传入数字则代表全部匹配内容
5.替换字符串
该功能用到regex_replace函数
string sStr; //要进行匹配的源字符串regex r; //正则表达式string toReplace; //进行替换的字符串string ret=regex_replace(sStr, r, toReplace)
- 1
- 2
- 3
- 4
该函数就是将sStr中匹配符合r的子字符串,将其全部替换为toReplace,并将结果返回到ret中
小技巧:可以在toReplace添加$n,n代表着第几个捕获组,可用于格式化字符串,总结如下:
6.分割字符串
这里主要使用到了sregex_token_iterator
regex r("-"); //以-为分隔符 string s = "yushi-csdn-yushi-csdn"; sregex_token_iterator beg(s.begin(), s.end(), r,-1); //传入-1,代表对匹配到的分隔符之间的内容感兴趣 sregex_token_iterator end; //结束标志 for (; beg != end; beg++) { cout << beg->str() << endl; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
用法基本于上述迭代器用法一样
唯一需要注意的是最后传入的那个-1,代表着我想要的是匹配项之间的内容
7.异常处理
regex库里已经实现了异常类regex_error ,直接使用即可,what函数将返回错误信息
try { regex r("\{\}"); } catch (const std::regex_error & e) { cout << e.what()<<endl; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
上述正则表达式使用grep语法,将{}进行转义,将输出以下错误:
regex_error(error_badrepeat): One of *?+{ was not preceded by a valid regular expression.
- 1
三、封装成类
可以看出来,原库函数使用起来很不方便,所以我花了点时间将上述几种常用功能封装成类,方便使用
#include<regex>#include<list>#include<string>class SRegex {private: std::regex r; std::wregex wr; SRegex(const SRegex& r) = delete;public: SRegex() {} /** * @brief 多字节字符构造函数 * @param pattern 匹配模式 */ SRegex(const std::string& pattern) { r = pattern; } /** * @brief 宽字节字符构造函数 * @param pattern 匹配模式 */ SRegex(const std::wstring& pattern) { wr = pattern; } /** * @brief (多字节)重新设置匹配模式 * @param pattern 匹配模式 */ void SetPattern(const std::string& pattern) { r = pattern; } /** * @brief (宽字节)重新设置匹配模式 * @param pattern 匹配模式 */ void SetPattern(const std::wstring& pattern) { wr = pattern; } /** * @brief 测试源字符串是否满足匹配模式(多字符) * @param sStr 源字符串 * @return 匹配返回true,否则返回false */ bool IsMatch(const std::string& sStr) { return std::regex_match(sStr, r); } /** * @brief 测试源字符串是否满足匹配模式(宽字符) * @param sWStr 源字符串 * @return 匹配返回true,否则返回false */ bool IsMatch(const std::wstring& swStr) { return std::regex_match(swStr, wr); } /** * @brief 获得所有满足匹配模式的子字符串(多字节字符) * @param res 获得匹配到的结果 * @param sStr 源字符串 * @param index 获取指定捕获组,默认为0,即全部 * @return 存在返回true,否则返回false */ bool GetSubStr(std::list<std::string>& res, const std::string& sStr, int index = 0) { res.clear(); std::string::const_iterator begin = sStr.begin(); std::string::const_iterator end = sStr.end(); std::smatch m; while (std::regex_search(begin, end, m, r)) { begin = m[0].second; res.push_back(std::move(m[index].str())); } return !res.empty(); } /** * @brief 获得所有满足匹配模式的子字符串(宽字符) * @param res 获得匹配到的结果 * @param sStr 源字符串 * @param index 获取指定捕获组,默认为0,即全部 * @return 存在返回true,否则返回false */ bool GetSubStr(std::list<std::wstring>& res, const std::wstring& sStr, int index = 0) { res.clear(); std::wstring::const_iterator begin = sStr.begin(); std::wstring::const_iterator end = sStr.end(); std::wsmatch m; while (std::regex_search(begin, end, m, wr)) { res.push_back(std::move(m[index].str())); } return !res.empty(); } /** * @brief 特换匹配到的子字符串(多字节) * @param sStr 源字符串 * @param toReplace 将要进行替换的字符串 * @return 返回替换成功的字符串 */ std::string Replace(const std::string& sStr, const std::string& toReplace) { return std::move(std::regex_replace(sStr, r, toReplace)); } /** * @brief 特换匹配到的子字符串(宽字节) * @param sStr 源字符串 * @param toReplace 将要进行替换的字符串 * @return 返回替换成功的字符串 */ std::wstring Replace(const std::wstring& sStr, const std::wstring& toReplace) { return std::move(std::regex_replace(sStr, wr, toReplace)); } /** * @brief 分割字符串(多字节) * @param sStr 需要进行分割的源字符串 * @return 返回分割后的字符串链表 */ std::list<std::string> Split(const std::string& sStr) { std::sregex_token_iterator beg(sStr.begin(), sStr.end(), r, -1); std::sregex_token_iterator end; std::list<std::string> res; for (; beg != end; beg++) { res.push_back(std::move(beg->str())); } return std::move(res); } /** * @brief 分割字符串(宽字节节) * @param sStr 需要进行分割的源字符串 * @return 返回分割后的字符串链表 */ std::list<std::wstring> Split(const std::wstring& sStr) { std::wsregex_token_iterator beg(sStr.begin(), sStr.end(), wr, -1); std::wsregex_token_iterator end; std::list<std::wstring> res; for (; beg != end; beg++) { res.push_back(std::move(beg->str())); } return std::move(res); }};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
因为我测试数据不多,可能存在bug,欢迎在评论区指出,我会及时更正,