C/C++ 正则表达式 regex库介绍详细版


前言

从C++11开始,C++开始支持正则表达式的使用,用于匹配字符串时非常方便!(比如从爬取的网页源码中提取指定的字符串匹配用户输入邮箱是否为正确格式替换一篇文章中指定的所有字符串

但正如大家所看到的,C++一如既往的使用模板实现,以至于我们使用的时候,如果出错了,很难看明白到底哪里出错了

所以本文对C++正则表达式库进行详细解析,便于大家的使用!

一、总体观摩

正则表达式库为regex,使用C++11及以上即可正常使用

#include<regex>

1.纵观所有类

regex文件里总共有7个模板类以及若干实例化的类

模板类 实例化类 用途
basic_regex regex:实际为basic_regex<char>,wregex:实际为basic_regex<wchar_t>,下面的类似,不再一一详细说明 作为正则表达式对象,用于匹配文本
match_results cmatchwcmatchsmatchwsmatch 用于获得匹配到的结果,实际可以看作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_iteratorwcregex_token_iteratorsregex_token_iteratorwsregex_token_iterator 用于遍历未匹配部分的迭代器
regex_error 报告正则表达式库生成的错误
regex_traits 描述用于匹配的元素的特征。一般用不上,有需求的可参考官方文档

虽然看着挺多,但其实平时真正用到的只有少数几个

特别需要注意的是,上面的类都是分类别配对使用的,比如:

regex ,cmatch,csub_match,cregex_iterator

就是一组

或者

regex ,smatch,ssub_match,sregex_iterator

也是一组

可以总结出的规律有:

  • 前缀没有w字母的为操作多字节字符,添加了w的则为操作宽字节字符
  • 前缀有c的,代表是操作char*类型字符串
  • 前缀有s的,代表是操作string类型字符串

注意,char*类型字符串与string类型实现类之间不能混用,否则会出错!

2.纵观所有函数

函数 用途
regex_match 匹配指定字符串整体是否符合
regex_search 匹配字符串中符合的子字符串
regex_replace 替换字符串中指定的字符串

二、使用详解

如何写正则表达式就不做细说,网上有很多教程,只对函数如何使用作出详细介绍

1.使用前需要了解的东西

下图为regex库默认使用的ECMAScript文法的表达式

在这里插入图片描述


在这里插入图片描述

如果想要更改为其它文法,只需要在regex构造函数中最后一位填入对应文法即可,例如:

regex r("<.*?>(.*)<.*?>", regex_constants::grep);

当然除了选择文法,还可以选择其它标志,只需要将他们用符号 | 连接起来即可

如忽略大小写匹配可以写为

regex r("<.*?>(.*)<.*?>", regex_constants::grep|regex_constants::icase);

可以看到,其实这些可选项都在regex_constants中,还有其它可选项如下:

在这里插入图片描述


不同文法之间的差异

在这里插入图片描述

还需要注意的是,C++中许多字符需要添加\ 符号进行转义才能使用,过于麻烦,所以C++11之后,出现了如下写法:

R"dem(内容)dem"

使用该写法就可以不再转义即可使用,其中dem为任意字符,但要求前后一致即可,其它为固定写法

2.测试字符串

该功能用到regex_match 函数

一般来说,最常用的就是下面这种写法:

比如测试用户输入字符串是否包含@符号:

regex r(".*@.*");
string str;
cin >> str;
if (regex_match(str, r)) {
	cout << "匹配成功";
}
else {
	cout << "匹配失败";
}

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表示匹配到的全部子字符串
		}

该代码段就是不断从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;
	}

由于sregex_iterator 默认构造函数为指向最后一个元素之后,所以对end没有进行任何处理,只是作为一个结束标志

成员函数str可以返回指定捕获组的字符串,不传入数字则代表全部匹配内容

5.替换字符串

该功能用到regex_replace函数

string sStr; //要进行匹配的源字符串
regex r; //正则表达式
string toReplace; //进行替换的字符串
string ret=regex_replace(sStr, r, toReplace)

该函数就是将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(),-1); //传入-1,代表对匹配到的分隔符之间的内容感兴趣
	sregex_token_iterator end; //结束标志
	for (; beg != end; beg++) {
		cout << beg->str() << endl;
	}

用法基本于上述迭代器用法一样

唯一需要注意的是最后传入的那个-1,代表着我想要的是匹配项之间的内容

在这里插入图片描述

7.异常处理

regex库里已经实现了异常类regex_error ,直接使用即可,what函数将返回错误信息

	try
	{
		regex r("\{\}");
	}
	catch (const std::regex_error & e)
	{
		cout << e.what()<<endl;
	}

上述正则表达式使用grep语法,将{}进行转义,将输出以下错误:

regex_error(error_badrepeat): One of *?+{ was not preceded by a valid regular expression.

三、封装成类

可以看出来,原库函数使用起来很不方便,所以我花了点时间将上述几种常用功能封装成类,方便使用

#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, 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, 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, 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(), -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(), -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);
	}
};

因为我测试数据不多,可能存在bug,欢迎在评论区指出,我会及时更正,

原文地址:https://blog.csdn.net/weixin_50964512

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


jquery.validate使用攻略(表单校验) 目录 jquery.validate使用攻略1 第一章&#160;jquery.validate使用攻略1 第二章&#160;jQuery.validate.js API7 Custom selectors7 Utilities8 Validato
/\s+/g和/\s/g的区别 正则表达式/\s+/g和/\s/g,目的均是找出目标字符串中的所有空白字符,但两者到底有什么区别呢? 我们先来看下面一个例子: let name = &#39;ye wen jun&#39;;let ans = name.replace(/\s/g, &#39;&#3
自整理几个jquery.Validate验证正则: 1. 只能输入数字和字母 /^[0-9a-zA-Z]*$/g jQuery.validator.addMethod(&quot;letters&quot;, function (value, element) { return this.optio
this.optional(element)的用法 this.optional(element)是jquery.validator.js表单验证框架中的一个函数,用于表单控件的值不为空时才触发验证。 简单来说,就是当表单控件值为空的时候不会进行表单校验,此函数会返回true,表示校验通过,当表单控件
jQuery.validate 表单动态验证 实际上jQuery.validate提供了动态校验的方法。而动态拼JSON串的方式是不支持动态校验的。牺牲jQuery.validate的性能优化可以实现(jQuery.validate的性能优化见图1.2 jQuery.validate源码 )。 也可
自定义验证之这能输入数字(包括小数 负数 ) &lt;script type=&quot;text/javascript&quot;&gt; function onlyNumber(obj){ //得到第一个字符是否为负号 var t = obj.value.charAt(0); //先把非数字的都
// 引入了外部的验证规则 import { validateAccountNumber } from &quot;@/utils/validate&quot;; validator.js /*是否合法IP地址*/ export function validateIP(rule, value,cal
VUE开发--表单验证(六十三) 一、常用验证方式 vue 中表单字段验证的写法和方式有多种,常用的验证方式有3种: data 中验证 表单内容: &lt;!-- 表单 --&gt; &lt;el-form ref=&quot;rulesForm&quot; :rules=&quot;formRul
正则表达式 座机的: 例子: 座机有效写法: 0316-8418331 (010)-67433539 (010)67433539 010-67433539 (0316)-8418331 (0316)8418331 正则表达式写法 0\d{2,3}-\d{7,8}|\(?0\d{2,3}[)-]?\d
var reg = /^0\.[1-9]{0,2}$/;var linka = 0.1;console.log (reg.test (linka)); 0到1两位小数正则 ^(0\.(0[1-9]|[1-9]{1,2}|[1-9]0)$)|^1$ 不含0、0.0、0.00 // 验证是否是[1-10
input最大长度限制问题 &lt;input type=&quot;text&quot; maxlength=&quot;5&quot; /&gt; //可以 &lt;input type=&quot;number&quot; maxlength=&quot;5&quot; /&gt; //没有效
js输入验证是否为空、是否为null、是否都是空格 目录 1.截头去尾 trim 2.截头去尾 会去掉开始和结束的空格,类似于trim 3.会去掉所有的空格,包括开始,结束,中间 1.截头去尾 trim str=str.trim(); // 强烈推荐 最常用、最实用 or $.trim(str);
正则表达式语法大全 字符串.match(正则):返回符合的字符串,若不满足返回null 字符串.search(正则):返回搜索到的位置,若非一个字符,则返回第一个字母的下标,若不匹配则返回-1 字符串.replace(正则,新的字符串):找到符合正则的内容并替换 正则.test(字符串):在字符串中
正整数正则表达式正数的正则表达式(包括0,小数保留两位): ^((0{1}.\d{1,2})|([1-9]\d.{1}\d{1,2})|([1-9]+\d)|0)$正数的正则表达式(不包括0,小数保留两位): ^((0{1}.\d{1,2})|([1-9]\d.{1}\d{1,2})|([1-9]+
JS 正则验证 test() /*用途:检查输入手机号码是否正确输入:s:字符串返回:如果通过验证返回true,否则返回false /function checkMobile(s){var regu =/[1][3][0-9]{9}$/;var re = new RegExp(regu);if (r
请输入保留两位小数的销售价的正则: /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/ 1.只能输入英文 &lt;input type=&quot;text&quot; onkeyup=&quot;value
判断价格的正则表达式 价格的正则表达式 /(^[1-9]\d*(\.\d{1,2})?$)|(^0(\.\d{1,2})?$)/; 1 解析:价格符合两种格式 ^ [1-9]\d*(.\d{1,2})?$ : 1-9 开头,后跟是 0-9,可以跟小数点,但小数点后要带上 1-2 位小数,类似 2,2
文章浏览阅读106次。这篇文章主要介绍了最实用的正则表达式整理,比如校验邮箱的正则,号码相关,数字相关等等,本文给大家列举的比较多,需要的朋友可以参考下。_/^(?:[1-9]d*)$/ 手机号
文章浏览阅读1.2k次。4、匹配中的==、an==、== an9、i9 == "9i"和99p==请注意下面这部分的作用,它在匹配中间内容的时候排除了说明:当html字符串如下时,可以匹配到两处,表示匹配的字符串不包含and且不包含空白字符。说明:在上面的正则表达式中,_gvim正则表达式匹配不包含某个字符串
文章浏览阅读897次。【代码】正则表达式匹配a标签的href。_auto.js 正则匹配herf