第十七章标准库设施
17.1 tuple类型
不同tuple的成员类型不同,且可以有任意数量的成员。每个tuple类型的成员数目是固定的,但一个tuple类型的成员数目可以与另一个tuple类型不同
17.1.1 定义和初始化tuple
tuple<size_t,size_t,size_t> threeD;
tuple<string,vector<double>,int,list<int>>
someVal("constants",{3.14,2.718},42,{0,1,2,3,4,5});
auto item = make_tuple("0-999-78345-X",3,20.00);
访问tuple成员
auto book = get<0>(item);
auto cnt = get<1>(item);
auto price = get<2>(item)/cnt;
get<2>(item) *= 0.8;
typedef decltype(item) trans;
size_t sz = tuple_size<trans>::value;//返回3
tuple_element<1,trans>::type cnt = get<1>(item);
17.2 bitset类型
17.2.1 定义和初始化bitset
bitset<32> bitvec(1U);//32位,低位位1,其他位为0
bitset<32> bitvec4("1100");//2 3位为1,其他为0
17.2.2 bitset操作
17.3 正则表达式
regex类表示一个正则表达式。除了初始化和赋值之外,regex还支持其他一些操作。
函数regex_match和regex_search确定一个给定字符序列与一个给定regex是否匹配。如果整个输入序列与表达式匹配,则regex_match函数返回true;如果输入序列中一个子串与表达式匹配,则regex_search函数返回true。
17.3.1 使用正则表达式库
string pattern("[^c]ei");
pattern = "[[:alpaha:]]*"+pattern+"[[:alpha:]]*";
regex r(pattern);//构造一个用于查找模式的regex
smatch results;//定义一个对象保存搜索结果
string test_str = "receipt freind theif receive";
if(regex_search(test_str,results,r))
cout << results.str() << endl;
指定regex对象的选项
当我们定义一个regex或是对一个regex调用assign为其赋予新值时,可以指定一些标志来影响regex如何操作。
正则表达式的错误
注意:一个正则表达式的语法是否正确是在运行时解析的。
常见错误类型如下:
正则表达式类和输入序列类型
输入可以是char数据或是wchar_t数据,字符可以保存在string中,或是char数组中。RE为这些不同的输入序列类型都定义了对应的类型,如下图:
17.3.2 匹配与Regex迭代器类型
string pattern("[^c]ei");
pattern = "[[:alpha:]]*"+pattern+"[[:alpha:]]*";
regex r(pattern,regex::icase);
for(sregex_iterator it(file.begin(),file.end(),r),end_it;it != end_it ;++it)
cout << it->str() << endl;
sregex_iterator绑定一个string和一个regex对象时,迭代器自动定位到给定string中第一个匹配位置。当解引用迭代器时,会得到一个对应最近一次搜索结果的smatch对象。当递增迭代器时,它调用regex_search在输入string中查找下一个匹配
17.3.3 使用子表达式
regex r("([[:alnum:]]+)\\.(cpp|cxx|cc)$",regex::icase);
if(regex_search(filename,results,r))
cout << results.str(1) << endl;//打印第一个子表达式
匹配对象除了提供匹配整体的相关信息以外,还提供访问模式中每个子表达式的能力。子匹配是按位置来访问的。第一个子匹配位置为0,表示整个模式对应的匹配,随后是每个子表达式对应的匹配。因此,本例中第一个子表达式,即表示文件名的子表达式,其位置为1,而文件扩展名对应的子表达式位置为2.
17.3.4 使用regex_replace
还可以在输入序列中查找并替换一个正则表达式,此时可以使用regex_replace.下图描述了regex_replace
string fmt = "$2.$5.$7";
regex r(phone);
string number="(908) 555-1800";
cout << regex_replace(number,r,fmt) << endl;
输出为:
908.555.1800
用来控制匹配和格式的标志
标准库还定义了用来在替换过程中控制匹配和格式的标志。如下图。这些标志可以传递给函数regex_search或regex_match或是类smatch的format成员
17.4 随机数
c++中跟随机数相关的两个概念有:随机数引擎和随机数分布。
随机数引擎用于生成unsigned随机数序列。
随机数分布用于对引擎生成的随机数进行特定的概率分布
17.4.1 随机数引擎和分布
default_random_engine e;
for(size_t i = 0;i< 10;++i)
cout << e() << " ";
下图列出了随机数引擎常见的操作
分布类型和引擎
为了得到一个指定范围内的数,我们使用一个分布类型的对象:
uniform_int_distribution<unsigned> u(0,9);
default_random_engine e;
for(size_t i = 0;i<10;++i)
cout << u(e) << " ";
uniform_int_distribution<unsigned>此类型生成俊宇分布的unsigned值。
注意:当我们说随机数发生器时,是指分布对象和引擎对象的组合
引擎生成一个数值序列
注意:一个给定的随机数发生器一直会生成相同的随机数序列。一个函数如果定义了局部的随机数发生器,应该将其定义为static的。否则,每次调用函数都会发生相同的序列
//几乎肯定是生成随机整数vector的错误方法
//每次调用这个函数都会生成相同的100个数
vector<unsigned> bad_randVec(){
default_random_engine e;
uniform_int_distribution<unsigned> u(0,9);
vector<unsiged> ret;
for(size_t i = 0;i<100;++i)
ret.push_back(u(e));
return ret;
}
正确的写法如下
vector<unsigned> good_randVec(){
static default_random_engine e;
static uniform_int_distribution<unsigned> u(0,9);
vector<unsigned> ret;
for(size_t i=0;i<100;++i)
ret.push_back(u(e));
return ret;
}
设置随机数发生器种子
一旦程序调试完毕,我们通常希望每次运行程序都会生成不同的随机结果,可以通过提供一个种子来达到这一目的。
default_random_engine e1;
default_random_engine e2(2147483646);
default_random_engine e3;
e3.seed(32767);
default_random_engine e4(32767);
for(size_t i=0;i!=100;++i)
if(e1() == e2())
cout << "unseeded match at iteration:" << i << endl;
if( e3()!= e4() )
cout << "seeded differs at iteration:" << endl;
17.4.2 其他随机数分布
随机数引擎生成unsigned数,范围内的每个数被生成的概率都是相同的。而应用程序常常需要不同类型或不同分布的随机数。标准库通过定义不同随机数分布对象来满足这两方面的要求,分布对象和引擎对象协同工作,生成想要的结果。下图列出了分布类型所支持的操作。
17.5 IO库再探
17.5.1 格式化输入和输出
每个iostream对象维护了一个格式状态来控制IO如何格式化的细节。
标准库定义了一组操纵符来修改流的格式状态,如下图
很多操纵符改变格式状态
当操纵符改变流的格式状态时,通常改变后的状态对所有后续IO都生效
17.5.2 未格式化的输入/输出操作
标准库还提供了一组底层操作,支持未格式化IO。这些操作允许我们将一个流当做一个无解释的字节序列来处理
单字节操作
如下图
将字符放回输入流
标准库提供了三种方法回退字符:
-
peek返回输入流中下一个字符的副本,但不会将他从流中删除,peek返回的值仍然留在流中
-
unget使得输入流向后移动,从而最后读取的值又回到流中。即使我们不知道最后从流中读取什么值,仍然可以使用unget
-
putback是更特殊的unget;它返回从六中读取的最后一个值,但他接受一个参数,此参数必须与最后读取的值相同
从输入操作返回的int
返回int的原因是:可以返回文件尾标记。我们使用char范围中的每个值来表示一个真实的字符,因此,取值范围没有额外的值可以用来表示文件尾
多字节操作
下图列出了多字节操作
17.5.3 流随机访问
标准库提供了一对函数,来定位(seek)到流中给定的位置,以及高速(tell)我们当前位置。
如下图
本章完