C++ Primer学习笔记-----第十七章:标准库特殊设施

几个标准库的使用:

1.tuple类型
在这里插入图片描述
1.1.定义和初始化tuple

tuple<int,int> t1;		//默认值初始化
tuple<int,string,vector<int>> t2(1,"a",{
    
    1,2});		//直接初始化
tuple<int, int> t3{
    
     1,2 };
auto t4 = make_tuple(1, "a");	//自动推断类型

访问tuple的成员

tuple<int,string> t(1,"a");
auto item = get<0>(t);	//第一个元素
get<1>(t) = "b";

typedef decltype(t) v;
size_t sz = tuple_size<v>::value;			//tuple中成员数量,返回2
tuple_element<1,v>::type cnt = get<1>(t);	//cnt是一个int,获取指定元素的类型

关系和相等运算符

为了使用tuple的相等或不等运算符,对每对成员使用==运算符必须都是合法的;
为了使用关系运算符,对每对成员使用<必须都是合法的。
tuple<int,int> t1(1,2);
tuple<int,int> t2(1,2);
bool b = (t1==t2);		
b = (t1<t2);
由于tuple定义了<==运算符,可以将tuple序列传递给算法,并且可以在无序容器中将tuple作为关键字类型。

1.2.使用tuple返回多个值

tuple<int, int> fun()
{
    
    
	return make_tuple(1, 2);
}

auto d = fun();
auto first = get<0>(d);

2.bitset类型

2.1.定义和初始化bitset
在这里插入图片描述
用unsigned值初始化bitset

bitset<4> b(3);
bitset<128> b2(~0ULL);	前面64位补0,后面64位为1~0ULL之后全为1

从一个string初始化bitset

bitset<8> bitvec("1100");	//2、3两位为1,剩余两位为0;
位值:0000 1100
bitset中的下标从低位到高位,顺序和字符串中的下标顺序是反的。

string s("1111111100000000");
bitset<32> b(s,5,4);	//从s[5]开始的四个二进制位
bitset<32> b2(s,s.size()-4);	//最后四个字符

在这里插入图片描述
2.2.bitset操作

在这里插入图片描述
3.正则表达式

在这里插入图片描述
在这里插入图片描述
3.1.使用正则表达式库

string s("[0-9]+");
regex r(s);
smatch result;
string str = "b123de4567";
if(regex_search(str, result, r))
	cout << result.str();

[[::alpha::]]	匹配任意字母
[[::alpha::]]*	匹配零个或多个字母
[[::alpha::]]+	匹配一个或多个字母

指定regex对象的选项
在这里插入图片描述

编写程序识别文件扩展名:
//一个或多个字母或数字字符后接一个.再接cpp或cxx或cc
string p = "[[:alnum:]]+\\.(cpp|cxx|cc)$";
regex r(p, regex::icase);
smatch result;
string fileName;
while (cin >> fileName)
	if (regex_search(fileName, result, r))
		cout << result.str() << endl;

由于.是通配符,所以需要加反斜杠,反斜杠也是C++中的特殊字符,因此再加一个反斜杠

指定或使用正则表达式时的错误

一个正则表达式的语法是否正确是在运行时解析的。

regex_error成员:
what():错误信息
code():错误类型对应的数值编码

try
{
    
    
	regex r("[[:alnum:]]+\\.(cpp|cxx|cc)$",regex::icase);
}
catch(regex_error e)
{
    
    
	cout<<e.what()<<endl;
	cout<<e.code()<<endl;
}

在这里插入图片描述
在这里插入图片描述
正则表达式类和输入序列类型
在这里插入图片描述

输入类型要匹配:处理什么输入序列类型,用什么类型的表达式类
string p = "[[:alnum:]]+.(cpp|cxx|cc)$";
regex r(p, regex::icase);
smatch result;
string fileName;
while (cin >> fileName)
	if (regex_search(fileName, result, r))	//这里的result只能是smatch,不能是cmatch等其他类型
		cout << result.str() << endl;

3.2.匹配与Regex迭代器类型
在这里插入图片描述在这里插入图片描述
使用sregex_iterator

查找字符串中的数字串
string p = "[0-9]+";
regex r(p);
smatch result;
string str = "abc123efo345op";
sregex_iterator end;
for (sregex_iterator it(str.begin(), str.end(), r); it != end; ++it)
{
    
    
	cout << it->str() << endl;
	cout <<"前子串"<<it->prefix().str() << "长度" << it->prefix().length() << endl;
	cout <<"后子串"<<it->suffix().str() << "长度" << it->suffix().length() << endl;
}

使用匹配数据
在这里插入图片描述
3.3.使用子表达式

string p = "([0-4]+)([5-9]+)";	//一个小括号代表一个子表达式,两个子表达式
regex r(p);
smatch result;
string str = "abc123789efo345op";
sregex_iterator end;
for (sregex_iterator it(str.begin(), str.end(), r); it != end; ++it)
{
    
    
	cout << it->str() <<" First: "<<it->str(1) << " Second: "<<it->str(2) <<endl;
}
例如:匹配的一个子串:123789str(0)),子串中第一个子表达式是123str(1)),第二个子表达式是789str(2)str(0)是第一个子匹配位置,表示整个模式对应的匹配

子表达式用于数据验证

string p = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ]?)(\\d{4})";
regex r(p);
smatch result;
string str = "abcd(123).123.1324";
sregex_iterator end;
for (sregex_iterator it(str.begin(), str.end(), r); it != end; ++it)
{
    
    
	cout << it->str() << endl;	//匹配(123).123.1324
	valid(*it);	//验证
}
(\\()?  		:匹配(
(\\d{
    
    3})		:匹配3个数字
(\\))?			:匹配)
([-. ])?		:匹配-.空格分隔符
(\\d{
    
    4})"		:匹配4个数字

使用子匹配操作
在这里插入图片描述

bool valid(const smatch& m)
{
    
    
	if(m[1].matched)	//有左括号,就得有有括号
		return m[3].matched && (m[4].matched == 0 || m[4].str() == " ");
	else				//没有左括号,也没有右括号
		return !m[3].matched && m[4].str() == m[6].str();	//分隔符必须匹配
}

3.4.使用regex_replace

在这里插入图片描述
替换输入序列的一部分

regex r("[0-9]+");	
string str("abc123aoe");
string fmt("BBB$1AAA");					//$1表示第一个子表达式,在第一个子表达式前面置BBB,后面置AAA
cout<<regex_replace(str, r, fmt);		//结果:abcBBBAAAaoe

用来控制匹配和格式的标志
在这里插入图片描述

使用格式标志

regex r("[0-9]+");	
string str("abc123aoe");
string fmt("BBB$1AAA")
cout<<regex_replace(str, r, fmt,format_no_copy);	//只拷贝它替换的文本,结果:BBBAAA

4.随机数

C++程序不应该使用库函数rand,应使用default_random_engine类和恰当的分部类对象。

4.1.随机数引擎和分布
在这里插入图片描述

default_random_engine e;
cout << e() << endl;		

分布类型和引擎

default_random_engine e(1);						//设置随机种子
uniform_int_distribution<unsigned> u(0, 9);		//设置范围
cout << u(e) << endl;

比较随机数引擎和rand函数

//下面的函数每次返回的值都是一样的
unsigned getRandom(unsigned min, unsigned max, unsigned seed = 0)
{
    
    
	default_random_engine e(seed);
	uniform_int_distribution<unsigned> u(min, max);
	return u(e);
}

静态对象会保存状态,所以会不一样
unsigned getRandom(unsigned min, unsigned max, unsigned seed = 0)
{
    
    
	static default_random_engine e(seed);
	static uniform_int_distribution<unsigned> u(min, max);
	return u(e);
}

4.2.其他随机数分布

生成随机实数

default_random_engine e(1);
uniform_real_distribution<double> u2(0,1);	//随机浮点数
cout << u(e) << endl;

在这里插入图片描述
使用分布的默认结果类型

uniform_real_distribution<> u(0,1);		//<>默认生成double值
uniform_int_distribution<> u(0, 9);		//默认生成int值

生成非均匀分布的随机数

正态分布
default_random_engine e;
normal_distribution<> n(4, 1.5);	//均值4,标准差1.5
n(e);

伯努利分布

default_random_engine e;
bernoulli_distribution b;	//普通类
b(e);		//默认是50/50的机会

bernoulli_distribution b(.55);		55/45的机会

5.IO库在探

5.1.格式化输入与输出
在这里插入图片描述在这里插入图片描述
输出补白
在这里插入图片描述

cout << left;	//左对齐
cout << "i:" << setw(6) << "AAA" << endl;		i:AAA
cout << right;	//右对齐
cout << "i:" << setw(6) << "AAA" << endl;		i:   AAA
cout << internal;	//用空白填充
cout << "i:" << setw(6) << "AAA" << endl;		i:   AAA
cout << setfill('#');	//用字符填充
cout << "i:" << setw(6) << "AAA" << endl;		i:###AAA

控制输入格式

空白符:空格符、制表符、换行符、换纸符、回车符
默认cin会忽略空白符	
noskipws:设置cin读取空白符

输入:
a b  c
d
默认输出:abcd
设置noskipws输出:
a b  c
d

5.2.未格式化的输入/输出操作

标准库还提供了一组底层操作,支持未格式化IO。这些操作允许我们将一个流当作一个无解释的字节序列来处理。

单字节操作
在这里插入图片描述

char ch;
while(cin.get(ch))
	cout.put(ch);

将字符放回输入流

cin.peek();
cin.unget();
cin.putback(1);

从输入操作返回的int值

int i;
while (i = cin.get() != EOF)
{
    
    
	cout.put(i);
}

多字节操作
在这里插入图片描述在这里插入图片描述

确定读取了多少个字符

在这里插入图片描述

5.3.流随机访问

随机IO本质上是依赖于系统的,为了理解如何使用这些特性,你必须查询系统文档。
标准库提供了一对函数来定位(seek)到流中给定的位置,已经告诉(tell)我们当前位置。
虽然标准库为所有流类型都定义了seek和tell函数,但它们是否会做有意义的事情依赖于流绑定到哪个设备。
在大多数系统中,cin、cout、cerr、clog的流不支持随机访问:当向cout直接输出数据时,类似向回跳十个
位置这种操作是没有意义的。对这些流我们可以调用seek、tell函数,但在运行时会出错,将流置于一个无效
状态。

由于istream和ostream类型通常不支持随机访问,剩余内容只适用于fstream和sstream类型。

seek和tell函数

为了支持随机访问,IO类型维护了一个标记来确定下一个读写操作要在哪里进行。
提供了两个函数:
seek:将标记定位到一个给定位置
tell:告诉我们标记的当前位置
标准库定义了两队seek和tell函数,一对用于输入流(后缀g,获得数据),另一对用于输出流(后缀p,放置数据)

在这里插入图片描述

只有一个标记

流中只维护单一的标记:并不存在独立的读标记和写标记

重定位标记

seekg(new_pos);			//绝对位置
seekg(offset,from);		//相对位置

访问标记

tellg();
fstream::beg
sstream::beg

例如:
fstream fs("path",fstream::ate|fstream::in|fstream::out);	//定位到文件尾
auto endPos= fs.tellg();		//获取文件尾位置
fs.seek(0,fstream::beg);	//从定位到文件开始位置
fs.seek(endPos)				//定位到文件尾
fs<<"abc";					
fs<<"abc\n";				//输入5个字符,\n添加了两个字符,最后一个字符是‘\n’

猜你喜欢

转载自blog.csdn.net/weixin_41155760/article/details/126142324
今日推荐