栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Day22:C++智能指针和正则表达式

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Day22:C++智能指针和正则表达式

目录

一、智能指针

1.智能指针的本质:

2.“何谓智能”:

3.三种常用的智能指针:

        ①shared_ptr

        ②weak_ptr

        ③unique_ptr

二、正则表达式的预备知识

        1.具有特殊意义的元字符

        2. 量词元字符

        3.校验数字的表达式 

        4.校验字符的表达式 

        5. 特殊需求表达式(copy时,去空格+改成双‘’)

 三、正则表达式在C++中的应用

        1.匹配  bool regex_match(string str,regex reg);        

        ①匹配用户输入的邮箱

        ②匹配某一个字符串(会区分大小写!)

        ③ 通过设置参数,设置不区分大小写字母

        ④字符串也可以直接充当正则规则(必须一一对应!!!)

        2. 正则替换string regex_replace(string str,regex reg, string newstr);

        ①正常匹配 (两种写法)

        ②控制替换,仅替换第一次出现的  regex_constants::format_first_only

        ③不拷贝方式(不返回与匹配内容无关的字段,相关的字段全部进行替换)                           regex_constants::format_no_copy        

        3.截取处理字符串       

                ① 法一:bool regex_search(string str,smatch result,regex reg) 

                                             不改变原字符串

                ②法二:srgex_iterator

                ③法三:                                                                                                                           sregex_token_iterator(iterator begin,iteartor end,regex regex, int flag);


一、智能指针

头文件:(管理内存)

#include

1.智能指针的本质:

        是一个模板类,一般使用是用的这个类的对象,而不是指针

2.“何谓智能”:

        体现在内存释放问题。用智能指针管理new的对象, 将不在需要手动delete

3.三种常用的智能指针:

        ①shared_ptr

              A.申请内存初始化及访问数据(实例化用的int类型而非int*!!!!,其余数据类型同理)

    //1.基本用法
	//new int(1111)申请内存做初始化
	//1.1 做初始化
	shared_ptr pInt(new int(1111));	
	//1.2 访问数据
	cout << *pInt << endl;  /
		cout << "带删除器写法....." << endl;
		shared_ptr p(new WBM[4], [](WBM* array) {delete[] array; });
	}
	shared_ptr pf(fopen("1.txt", "w"), freeFile);

输出:

带删除器写法.....
析构函数被调用
析构函数被调用
析构函数被调用
析构函数被调用
文件释放成功

        ②weak_ptr

                A.弱引用指针,不会累计计数(应用场景比较单一)

                B.weak_ptr只能通过shared_ptr或者weak_ptr来构造(不能直接管理数据)

                C.主要应用场景: 为了解决shared_ptr 循环引用内存导致无法释放问题

                D.不可使用* 取值,能使用->取值

                E.通过成员函数lock获取shared_ptr对象 然后再访问数据

                   例1:常规使用(访问数据的方法)

class MM 
{
public:
	MM() {}
	void print() 
	{
		cout << "打印" << endl;
	}
	~MM() { cout << "析构" << endl; }
protected:
};    
    shared_ptr pMM(new MM);
	shared_ptr pMM2 = pMM;
	cout << "计数:" << pMM2.use_count() << endl;
	weak_ptr pwMM(pMM);
	cout << "计数:" << pMM2.use_count() << endl;
	//(*pwMM).print();//weak_ptr不存在这种访问方式
	//pwMM.lock(): shared_ptr对象
	pwMM.lock().get()->print();
	(pwMM.lock())->print();

  输出:

   计数:2
   计数:2
   打印
   打印

                        例2:应用场景(解决share_ptr的循环引用问题->内存泄露)

class B;
class A 
{
public:
	A() { cout << "A" ;}
	~A() { cout << "~A"; }
	weak_ptr b;
};
class B 
{
public:
	B() { cout << "B"; }
	~B() { cout << "~B"; }
	weak_ptr a;
};
void testLoopUse() 
{
	cout << "循环引用....." << endl;
	shared_ptr aObject(new A);
	shared_ptr bObject(new B);
	aObject->b = bObject;
	bObject->a = aObject;
	cout << endl << "计数:" << bObject.use_count() << endl;
	cout << endl;
}

输出: 

循环引用.....
AB
计数:1

~B~A
12
析构

        ③unique_ptr

                A.禁止拷贝和赋值,独占型(无use_count方法!)

                B.也可以用make_unique去构造出对象

    unique_ptr pMM(new MM(1111));
	unique_ptr mynum = make_unique(666);
	//unique_ptr pMM2(pMM);			// 错误
	unique_ptr pMM3;	
	//pMM3 = pMM2;						// 错误
	cout << "MM num:" << pMM->getNum() << endl;
	cout << "MM num:" << pMM.get()->getNum() << endl;

                C.任何时候unqiue_ptr操作管理对象,永远都只有一个有效

                        (i)可以通过move函数转交所有权 (原ptr就不能再去访问了!!)两种写法(move语句ormove构造)转接所有权后,可以用reset重新赋值。

                        (ii)reset函数结合release函数移交所有权(不用release本质上也是一种赋值,所以会报错。)

class MM 
{
public:
	MM() {}
	MM(int num) :num(num) {}
	int getNum() { return num; }
	~MM() { cout << "MM:" << num << endl; };
protected:
	int num=0;
};
    //move转交所有权
	pMM3 = move(pMM);
	cout << "MM num:" << pMM3->getNum() << endl;
	//cout << "MM num:" << pMM->getNum() << endl; // pMM不在管理对象
	unique_ptr pMM4(move(pMM3));  //MM&& 

	//可以重新设置
	pMM.reset(new MM(1));
	cout << "MM num:" << pMM->getNum() << endl;
	unique_ptr pMM5;
	pMM5.reset(pMM4.release());
	cout << pMM5->getNum() << endl;

                D.访问数据:有get方法,也可以直接当做一个指针使用。

                E:构造unique_ptr对象时除了传递指针外,还可以提供一个删除器。如果没有指定删除器,unique_ptr会使用缺省删除器,即缺省使用delete和delete[]来销毁对象,分别用于普通版本和数组版本。如果动态分配的资源有专门的释放函数,必须在构造时同时提供一个删除器。比如:

unique_ptr up2(new int(4), 
    [](int *ptr){delete ptr;});
unique_ptr> ar_up2(new int[10],
     [](int *ptr){delete[] ptr;});

注: 删除器要求是可调用对象,它的类型可以是函数指针、函数对象、lambda表达式、function对象等。->下面是仿函数释放+lambda释放的方法

class MM 
{
public:
	MM() {}
	MM(int num) :num(num) {}
	int getNum() { return num; }
	~MM() { cout << "MM:" << num << endl; };
protected:
	int num=0;
};
struct DeleteMM 
{
	void operator()(MM* mm) 
	{
		delete[]  mm;
		cout << "释放成功...." << endl;
	}
};
	//删除器
	{
		unique_ptr p(new MM[3]);
		unique_ptr pp(new MM[3],
			[](MM* mm) {delete[]mm; });
	}

当使用提供删除器时,删除器的类型也要作为模板的参数类型声明在unique_ptr中,也就是说删除器类型也是unique_ptr模板实例化后的类型的一部分。因此,如果一个unique_ptr对象在move时,源和目的unique_ptr对象的删除器类型也必须一样,这点与shared_ptr不一样

一般情况下,unique_ptr释放的资源是在内存中动态分配的,但通过定制删除器,也可以使用unique_ptr来管理一些通过其他方式分配的资源,比如文件句柄、socket等。C++中有一种资源管理的惯例-RAII,可以通过它来实现对资源的自动释放,比如,对于使用fopen打开的文件,返回一个文件指针FILE,在程序结束时,需要调用fclose关闭,为了防止忘记关闭,可以手动编写一个RAII类来负责管理这个FILE的生命周期,不是很方便。现在可以使用unique_ptr来实现,只需定义一个删除器就可以了,非常方便。下面是一个使用unique_ptr来自动关闭打开文件的例子

FILE *file = fopen("/tmp/tmp.txt", "r"); // 分配FILE资源
unique_ptr up(file, [](FILE *file) {
	fclose(file);
});
...//其它操作file的代码逻辑

二、正则表达式的预备知识

        1.具有特殊意义的元字符

 :字符能够改变字符原本的含义

^:^字符指示字符串的头,且要求字符串以字符开头,不占位。^表示一个真正的^符号。

$:$字符指示字符串的尾,且要求字符串以字符结尾,不占位。\$表示一个真正的$符号。

():分组,大正则中包含小正则。可以改变默认的优先级。在模式中可以使用1来表示第一组已然捕获到的东西,2来表示第二组已然捕获到的东西。

b:指示字符串的边界(头/尾/空格左/空格右),字符b要求边界的左边是字符,b字符要求边界的右边是字符。

.:表示一个除了n以外的任意一个字符。\.表示一个真正的.符号。

|:a|b  a或b之一

[abc]:abc之中的任意一个

[^abc]:  abc之外的

[a-z]:    任意小写字母

[^a-z]:  除了小写字母之外的

w:任意一个字母数字下划线,等价于[(0-9)(a-z)(A-Z)(_)]

W:字母数字下划线之外的,等价于[]

d:    任意一个数子

D:    除了数字之外的

s:    空白符(空格、制表符、换页符)

        2. 量词元字符

*:字符*要求字符出现0到多次 {0,}

+:字符+要求字符出现1到多次 (w) {1,}

?:字符?要求字符出现0次或1次 {0,1}

{n}:字符{n}要求字符出现n次

{n,}:字符{n,}要求字符出现n到多次 {0,}

{n,m}:字符{n,m}要求字符出现n到m次、

所以含有的元字符,在C++定义时,都要写成\   

        3.校验数字的表达式 

数字:^ [0 - 9] * $

n位的数字:^ d{ n }$

至少n位的数字:^ d{ n, }$

m - n位的数字: ^ d{ m,n }$

零和非零开头的数字: ^ (0 | [1 - 9][0 - 9] *)$

非零开头的最多带两位小数的数字: ^ ([1 - 9][0 - 9] *) + (.[0 - 9]{ 1,2 }) ? $

带1 - 2位小数的正数或负数: ^ ( - ) ? d + (.d{ 1,2 }) ? $

正数、负数、和小数: ^ ( - | + ) ? d + (.d + ) ? $

有两位小数的正实数: ^ [0 - 9] + (.[0 - 9]{ 2 }) ? $

有1~3位小数的正实数: ^ [0 - 9] + (.[0 - 9]{ 1,3 }) ? $

非零的正整数: ^ [1 - 9]d * $ 或 ^ ([1 - 9][0 - 9] *) { 1, 3 }$ 或^ + ? [1 - 9][0 - 9] * $

非零的负整数: ^ - [1 - 9][]0 - 9"$ 或 ^-[1-9]d$

非负整数: ^ d + $ 或 ^ [1 - 9]d * | 0$

非正整数: ^ -[1 - 9]d * | 0$ 或 ^ ((-d + ) | (0 + ))$

非负浮点数: ^ d + (.d + ) ? $ 或 ^ [1 - 9]d * .d * | 0.d * [1 - 9]d * | 0 ? .0 + | 0$

非正浮点数: ^ ((-d + (.d + ) ? ) | (0 + (.0 + ) ? ))$ 或 ^ (-([1 - 9]d * .d * | 0.d * [1 - 9]d*)) | 0 ? .0 + | 0$

正浮点数: ^ [1 - 9]d * .d * | 0.d * [1 - 9]d * $ 或 ^ (([0 - 9] + .[0 - 9] * [1 - 9][0 - 9] *) | ([0 - 9] * [1 - 9][0 - 9] * .[0 - 9] + ) | ([0 - 9] * [1 - 9][0 - 9] *))$

负浮点数: ^ -([1 - 9]d * .d * | 0.d * [1 - 9]d*)$ 或 ^ (-(([0 - 9] + .[0 - 9] * [1 - 9][0 - 9] *) | ([0 - 9] * [1 - 9][0 - 9] * .[0 - 9]) | ([0 - 9] * [1 - 9][0 - 9] *)))$

浮点数: ^ (-? d + )(.d + ) ? $ 或 ^ -? ([1 - 9]d * .d * | 0.d * [1 - 9]d * | 0 ? .0 + | 0)$

        4.校验字符的表达式 

汉字: ^ [u4e00 - u9fa5]{ 0, }$
英文和数字: ^ [A - Za - z0 - 9] + $ 或 ^ [A - Za - z0 - 9]{ 4,40 }$
长度为3 - 20的所有字符: ^ .{3, 20}$
由26个英文字母组成的字符串: ^ [A - Za - z] + $
由26个大写英文字母组成的字符串: ^ [A - Z] + $
由26个小写英文字母组成的字符串: ^ [a - z] + $
由数字和26个英文字母组成的字符串: ^ [A - Za - z0 - 9] + $
由数字、26个英文字母或者下划线组成的字符串: ^ w + $ 或 ^ w{ 3,20 }$
中文、英文、数字包括下划线: ^ [u4E00 - u9FA5A - Za - z0 - 9_] + $
中文、英文、数字但不包括下划线等符号: ^ [u4E00 - u9FA5A - Za - z0 - 9] + $ 或 ^ [u4E00 - u9FA5A - Za - z0 - 9]{ 2,20 }$
可以输入含有 ^ %&',;=?$"等字符:[^%&', ; = ? $x22] + 12

禁止输入含有~的字符:[^ ~x22] +

        5. 特殊需求表达式(copy时,去空格+改成双‘’)

Email地址: ^ w + ([-+.]w + ) * @w + ([-.]w + ) * .w + ([-.]w + ) * $
域名:[a - zA - Z0 - 9][-a - zA - Z0 - 9]{ 0,62 }(/ .[a - zA - Z0 - 9][-a - zA - Z0 - 9]{ 0,62 }) + / . ?
InternetURL:[a - zA - z] + ://[^s]* 或 ^http://([w-]+.)+[w-]+(/[w-./?%&=]*)?$
手机号码: ^ (13[0 - 9] | 14[5 | 7] | 15[0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9] | 18[0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9])d{ 8 }$
电话号码(0511 - 4405222、021 - 87888822):d{ 3 } - d{ 8 } | d{ 4 } - d{ 7 }
身份证号(15位、18位数字): ^ d{ 15 } | d{ 18 }$
短身份证号码(数字、字母x结尾): ^ ([0 - 9]) { 7, 18 }(x | X) ? $ 或 ^ d{ 8,18 } | [0 - 9x]{ 8,18 } | [0 - 9X]{ 8,18 } ? $
帐号:(字母开头,允许5 - 16字节,允许字母数字下划线): ^ [a - zA - Z][a - zA - Z0 - 9_]{ 4,15 }$
密码:(以字母开头,长度在6~18之间,只能包含字母、数字和下划线): ^ [a - zA - Z]w{ 5,17 }$
强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8 - 10之间):* ^ (? = .*d)(? = .*[a - z])(? = .*[A - Z]).{8, 10}$
日期格式: ^ d{ 4 } - d{ 1,2 } - d{ 1,2 }
一年的12个月(01~09和1~12): ^ (0 ? [1 - 9] | 1[0 - 2])$
一个月的31天(01~09和1~31): ^ ((0 ? [1 - 9]) | ((1 | 2)[0 - 9]) | 30 | 31)$
xml文件: ^ ([a - zA - Z] + -? ) + [a - zA - Z0 - 9] + \.[x | X][m | M][l | L]$
中文字符的正则表达式:[u4e00 - u9fa5]
双字节字符:[^ x00 - xff](包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
空白行的正则表达式:ns * r(可以用来删除空白行)
HTML标记的正则表达式:<(S* ? )[^ > ] *>.* ? < / 1> | <.* ? / > (复杂的嵌套标记依旧无能为力)
首尾空白字符的正则表达式: ^ s * | s * $或(^ s*) | (s * $) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等))
腾讯QQ号:[1 - 9][0 - 9]{ 4, } (腾讯QQ号从10000开始)
中国邮政编码:[1 - 9]d{ 5 }(? !d) (中国邮政编码为6位数字)
IP地址:d + .d + .d + .d + (提取IP地址时有用)
IP地址:((? : (? : 25[0 - 5] | 2[0 - 4]\d | [01] ? \d ? \d)\.) { 3 }(? : 25[0 - 5] | 2[0 - 4]\d | [01] ? \d ? \d))

 三、正则表达式在C++中的应用

        1.匹配  bool regex_match(string str,regex reg);        

                匹配返回: true  不满足返回false

                ①匹配用户输入的邮箱
regex reg("^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
	string userName;
	while (1)
	{
		cout << "请输入一个邮箱:";
		cin >> userName;
		bool result = regex_match(userName, reg);
		if (result)
		{
			cout << "正确邮箱" << endl;
			break;
		}
	}

输入输出:

请输入一个邮箱:b19031822@njupt.com.cn
正确邮箱

        ②匹配某一个字符串(会区分大小写!)
    //正则匹配,一定是完全匹配
	string str = "ILoveyou1314";
	regex  reg("[a-z0-9]+");    //小写祖母或者数字多个
	if (regex_match(str, reg))
	{
		cout << "匹配" << endl;
	}
	else
	{
		cout << "不匹配" << endl;  //IL 不满足正则规则
	}

 输出:不匹配

        ③ 通过设置参数,设置不区分大小写字母
	//大小写问题 ,构造的时候可以忽略大小写
	string str = "ILoveyou1314";
	regex  reg("[a-z0-9]+");    //小写祖母或者数字多个
	regex reg2("[a-z0-9]+", regex_constants::icase);
	if (regex_match(str, reg2))
	{
		cout << "匹配" << endl;
	}
	else
	{
		cout << "不匹配" << endl;  //IL 不满足正则规则
	}

输出:匹配

         ④字符串也可以直接充当正则规则(必须一一对应!!!)
	//字符串也可以直接充当正则规则
	string str = "ILoveyou1314";
	regex reg3("ILoveyou");
	if (regex_match(str, reg3))
	{
		cout << "匹配" << endl;
	}
	else
	{
		cout << "不匹配" << endl;  //1314
	}

输出:不匹配

        2. 正则替换string regex_replace(string str,regex reg, string newstr);

        注:并不会修改str原字符串

        ①正常匹配 (两种写法)

        法一:缺省,将匹配到的所有数字,都进行replace

    string  str = "ILoveyou1314520IMissyou520me";
	regex reg("\d+");//匹配所有的数字!
	cout << "str:" << regex_replace(str, reg, "我爱你") << endl;
	cout << "oldstr:" << str << endl;

输出:

str:ILoveyou我爱你IMissyou我爱你me
oldstr:ILoveyou1314520IMissyou520me

        法二:传入传参的默认参数  regex_constants::format_default

    string  str = "ILoveyou1314520IMissyou520me";
    regex reg("\d+");//匹配所有的数字!
    //默认方式
	cout << "default:" << regex_replace(str, reg, "我爱你", 
        regex_constants::format_default)<< endl;

输出: default:ILoveyou我爱你IMissyou我爱你me

        ②控制替换,仅替换第一次出现的  regex_constants::format_first_only
    string  str = "ILoveyou1314520IMissyou520me";
	regex reg("\d+");//匹配所有的数字!
    //1.只替换第一次出现的
	cout << "only first:" << regex_replace(str, reg, "我爱你",                                 
         regex_constants::format_first_only<< endl;

输出:only first:ILoveyou我爱你IMissyou520me

         ③不拷贝方式(不返回与匹配内容无关的字段,相关的字段全部进行替换)                           regex_constants::format_no_copy        
    string  str = "ILoveyou1314520IMissyou520me";
	regex reg("\d+");//匹配所有的数字!
	//2.不拷贝
	cout << "no copy:" << regex_replace(str, reg, "我爱你",
         regex_constants::format_no_copy)<< endl;

        3.截取处理字符串       

                ① 法一:bool regex_search(string str,smatch result,regex reg) 

                                                        不改变原字符串
    //正常截取处理 匹配项
	string str = "ILoveyou1314IMissyou520me";
	smatch result;
	bool flag = regex_search(str, result, regex("\d+"));
	if (flag)
	{
		cout << "size:" << result.size() << endl;
		for (int i = 0; i < result.size(); i++)
		{
			cout << result.str() << endl;  //字符串
		}
		cout << "pre:" << result.prefix() << endl;
		cout << "suf:" << result.suffix() << endl;
	}
	cout << "str:" << str << endl;
	//匹配后面: result.suffix作为循环条件去持续截即可

输出:

size:1
1314
pre:ILoveyou
suf:IMissyou520me 

                ②法二:srgex_iterator

                      操作类似于istream_iterator 

    string str = "ILoveyou1314IMissyou520me";
    regex rule("\d+");
	sregex_iterator pos(str.begin(), str.end(), rule);
	sregex_iterator end;   //end_of_iterator
	while (pos != end)
	{
		cout << pos->str() << endl;
		pos++;
	}
	//拆解字符串;

输出: 

1314
520

                ③法三:                                                                                                                           sregex_token_iterator(iterator begin,iteartor end,regex regex, int flag);

                                flag: 0: 所有匹配的,-1 存储所有不匹配的   

                                操作类似于istream_iterator 

    //sregex_token_iterator(iterator begin,iteartor end,regex regex, int flag);
	//flag: 0: 所有匹配的,-1 存储所有不匹配的
    string str = "ILoveyou1314IMissyou520me";
	regex regexRule("\d+");
	sregex_token_iterator Begin(str.begin(), str.end(), regexRule, 0);
	sregex_token_iterator End;
	while (Begin != End)
	{
		cout << Begin->str() << endl;
		Begin++;
	}

输出:

1314
520 

                        

              

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1001005.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号