S17标准库特殊设施
注意:本章的内容没有过多深入,日后用到再参照学习
一、tuple
类型
一个tuple
可以有任意类型任意数量的成员,确定的tuple
类型的成员数量是固定的,tuple
类型及其伴随类型和函数定义在头文件tuple中,基本操作如下
tuple<T1, T2, .., Tn> t; //t是一个tuple,成员数为n,第i个成员的类型是Ti,所有成员进行值初始化
tuple<T1, T2, .., Tn> t(v1, v2, .., vn); //同上,每个成员用对应的初始值vi进行初始化,此构造是explicit的
make_tuple(v1, v2, .., vn) //返回一个给定初始值初始化的tuple,类型从初始值的类型推断
t1 == t2 //当两个tuple具有相同数量的成员且成员对应相等时,才相等
t1 != t2
t1 </<=/>/>= t2 //基于字典序,两个tuple必须具有相同数量的成员,并用<依次比较
get<i>(t) //返回t的第i个数据成员的引用,如果t是一个左值,结果是一个左值引用
//否则返回一个右值引用,tuple的所有成员都是public的
tuple_size<tupleType>::value //一个类模板,通过一个tuple类型初始化
//这个类有一个名为value的public constexpr static数据成员
//类型为size_t,表示给定tuple类型中成员的数量
tuple_element<i, tupleType>::type //一个类模板,通过一个整型i和一个tuple类型初始化
//这个类有一个名为type的public成员,表示给定tuple类型中指定成员的类型
1、定义和初始化tuple
(1)定义和初始化
定义一个tuple
时必须指出每个成员的类型,并且默认进行值初始化或者用直接初始化,不可以赋值构造
tuple<string, vector<double>, int, list<ing>> someVal("constants", {3.14, 2.718}, 42, {0,1,2,3,4});
tuple<size_t, size_t> twoD = {1, 2}; //错误
tuple<size_t, size_t> twoD{1, 2}; //正确
(2)访问tuple
成员
使用get
函数模板访问tuple
成员,get<i>()
尖括号中必须指出想访问的第几个成员,是整型常量表达式,圆括号中传递给get
一个tuple
对象,get
返回指定成员的引用
auto book = get<0>(item); //返回item的第一个成员
get<2>(item) *= 0.8; //将item的第三个成员乘以0.8
//不知道tuple具体信息时可以利用以下两个辅助模板
size_t sz = tuple_size<decltype(item)>::value; //返回item所属类型的对象中成员的数量
tuple_element<1, decltype(item)>::type cnt = get<1>(item); //cnt是int,cnt前是指定第二个元素的类型
(3)关系和相等运算符
类似其他容器的操作,对tuple
也是逐对比较两个tuple
对应的成员,但额外要求两个tuple
必须成员数量也相等,要求都定义了==/<
运算
注意:由于tuple
定义了</==
运算,因此可以将tuple
序列传递给算法,也可以把tuple
作为无序容器的关键字
2、使用tuple
返回多个值,参考《C++primer》p.638
二、bitset
类型
1、定义和初始化bitset
bitset
类用来处理二进制位集合,定义在bitset头文件中,能够处理超过最长整型类型大小的集合
bitset<n> b; //b有n位,n必须是常量表达式,b的每一位都是0,此构造是一个constexpr
bitset<n> b(u); //b是unsigned long long值u的低n位的拷贝
//如果n大于unsigned long long,则超出部分均为0,此构造是一个constexpr
bitset<n> b(s, pos, m, zero, one); //b是string s从pos开始m个字符的拷贝,s只能包含zero/one
//否则抛出invalid_argument异常,字符在b中分别保存为zero/one
//pos默认0,m默认string::nops,zero默认'0',one默认'1'
bitset<n> b(cp, pos, m, zero, one); //从cp指向的字符数组中拷贝字符,若未提供m则cp必须指向C风格字符串
//若提供了m则cp开始必须至少有m个zero/one
(1)用unsigned
值初始化bitset
当用一个整型值来初始化时,自动转换为unsigned long long
并被当作位模式来处理,超出填0,少于则直接忽略多出的位
bitset<13> bitvec1(0xbeef); //bitvec1 = 1 1110 1110 1111
(2)从一个string
初始化bitset
若string
包含的字符数比bitset
少则高位置0
bitset<5> bitvec4("1100"); //01100
string str("1111111000000011001101");
bitset<32> bitvec5(str, 5, 4); //从str[5]开始的4个二进制位
bitset<32> bitvec6(str, str.size()-4); //使用最后4个字符
注意:bitset的二进制位串的索引是从低位开始是0,即最右侧的元素为0,与字符串等正好相反
2、bitset
操作
(1)对二进制位串提供了诸如检测是否有1等各种操作,参考《C++primer》p.643
(2)提取bitset
的值
to_ulong/to_ullong //操作返回bitset对象相同位模式对应的值
//只有当bitset对象的大小小于等于对应的大小才能完成转换,否则抛出overflow_error异常
unsigened long ulong = bitvec3.to_ulong();
(3)bitset
的IO运算符
输入运算符从流中读取字符保存到临时的string
中,当遇到非0/1
、EOF
或读满时读取结束并将string
转换为bitset
bitset<16> bits;
cin >> bits; //从cin最多读取16位0/1
cout << bits << endl; //打印bits中的0/1串
(4)使用bitset
,参考《C++primer》p.644
三、正则表达式
正则表达式是一种描述字符序列的方法,RE库定义在头文件regex中,包含多个组件如下
regex //表示有一个正则表达式类
regex_match //将一个字符序列与一个正则表达式匹配
regex_search //寻找第一个与正则表达式匹配的子序列
regex_replace //使用给定格式替换一个正则表达式
sregex_iterator //迭代器适配器,调用regex_search来遍历一个string中所有匹配的子串
smatch //容器类,保存在string中搜索的结果
ssub_match //string中匹配的子表达式的结果
如果整个输入序列与表达式匹配,则regex_match
返回true
,如果输入序列中有一个子串与表达式匹配,则regex_search
返回true
,regex_search/regex_match
的参数表为(seq, m, r, mft)
或(seq, r, mft)
,seq
为待查找的输入序列,可以是string
、表示范围的一对迭代器或字符数组的指针,m
是一个match
对象可以保存匹配的细节(m
和seq
必须兼容),mft
是一个可选的regex_constants::match_flag_type
值,会影响匹配过程
1、使用正则表达式库
string pattern("[^c]ei"); //正则表达式,查找不在c之后的字符串ei
pattern = "[[:alpha:]]*" + pattern + "[[:alpha:]]*"; //匹配模式
regex r(pattern); //构造一个用于查找模式的regex
smatch results; //smatch对象保存搜索结果
string test_str = "receipt freind theif receive"; //待匹配的字符串
if(regex_search(test_str, results, r))
cout << results.str() << endl; //匹配并输出结果friend
(1)指定regex
对象的选项,参考《C++primer》p.647
(2)指定或使用正则表达式时的错误,参考《C++primer》p.648
注意:一个正则表达式的语法正确与否是在运行时解析的,如果错误则抛出regex_error
异常
注意:正则表达式的构造、赋值等开销非常大,尽可能避免使用,不要在循环中重复创建、赋值,另外可以尝试boost/regex
(3)正则表达式类和输入序列类型
输入序列类型 | 应使用的正则表达式类 |
---|---|
string | regex/smatch/ssub_match/sregex_iterator |
const char * | regex/cmatch/csub_match/cregex_iterator |
wstring | wregex/wsmatch/wssub_match/wsregex_iterator |
const wchar_t * | wregex/wcmatch/wcsub_match/wcregex_iterator |
2、匹配与regex
迭代器类型
(1)sregex_iterator
迭代器的操作,见《C++primer》p.650
(2)使用sregex_iterator
//在1中的代码继续
regex r(pattern, regex::icase); //icase:匹配时忽略大小写
//sregex_iterator end_it默认构造直接生成尾后迭代器
for(sregex_iterator it(test_str.begin(), test_str.end(), r), end_it; it != end_it; ++it)
cout << it->str() << endl; //输出所有匹配的单词
(3)使用匹配数据,smatch
及相关操作,见《C++primer》p.653
3、使用子表达式
正则表达式中的模式通常包含一个或多个子表达式,一个子表达式是模式的一部分,本身也具有意义,通常用括号分组来表示子表达式
//子表达式1:([[:alnum:]]+),匹配一个或多个字符的序列
//子表达式2:(cpp|cxx|cc),匹配文件扩展名
regex r("([[:alnum:]]+)\\.(cpp|cxx|cc)$", regex::icase);
.. cout << results.str(1) << endl; //打印第一个子匹配(索引为1)即只打印文件名,索引为0代表整个匹配
注意:匹配对象除了提供整体的相关信息外,还提供访问模式中每个子表达式的能力,参考《C++primer》p.654
4、使用regex_replace
,电话号码替换案例,参考《C++primer》p.657
(1)只替换输入序列的一部分
正则表达式的替换只会替换匹配的对象,对没有匹配上的对象不会有作用,例如课本例子"morgan (201) 555-236" -> "morgan 201.555.236"
,其中匹配上的(201) 555-236
根据需求替换,而没有匹配的morgan
不变
(2)用来控制匹配和格式的标志,参考《C++primer》p.658
类似标准库定义标志来指导如何处理正则表达式,标准库也定义了在替换过程中控制匹配或格式的标志
(3)使用格式标志
string fmt2 = "$2.$5.$7 ";
cout << regex_replace(s, r, fmt2, format_no_copy) << endl; //format_no_copy不输出未匹配的部分
四、随机数
随机数引擎类,生成随机unsigned
整数序列,随机数分布类,使用引擎返回服从特定概率分布的随机数,均定义在random头文件中,C++程序不应该再使用C的rand
而是default_random_engine
和恰当的分布类对象
1、随机数引擎和分布
(1)随机数引擎是函数对象类,定义调用运算符,不接受参数并返回随机unsigned
整数,参考《C++primer》p.660
(2)分布类型和引擎,分布类型也是函数对象类,接受一个随机数引擎作为参数,并映射到指定的分布
uniform_int_distribution<unsigned> u(0, 9); //生成0-9之间均匀分布的随机数
default_random_engine e; //生成无符号随机数
cout << u(e) << endl; //分布类型对象u接受一个随机数引擎e返回一个指定范围的随机数
(3)比较随机数引擎和rand
函数
注意:一个引擎的范围可以通过该类型对象调用min/max
成员来获得
(4)引擎生成一个数值序列,参考《C++primer》p.661
(5)设置随机数发生器种子
希望每次运行程序都会生成不同的随机结果,可以通过提供一个种子来达到这一目的,在创建引擎对象时提供种子或是调用引擎的seed
成员
2、其他随机数分布,参考《C++primer》p.663
(1)生成随机实数
(2)使用分布的默认结果类型
(3)生成非均匀分布的随机数
(4)不接受模板参数的普通类bernouli_distribution
五、IO库再探
定义在iostream中的操纵符见《C++primer》p.669
1、格式化输入与输出
(1)很多操纵符改变格式状态
操纵符用于两大类输出控制:控制数值的输出形式以及控制补白的数量和位置,大多数操纵符都是设置/复原成对的
注意:当操纵符改变流的格式状态时,改变后的状态对所有后续IO都有效,因此在不再需要特殊格式时尽快复原
(2)控制布尔值的格式:boolalpha/noboolalpha
,将bool
值的输出从1/0
改为true/false
,noboolalpha
复原
cout << boolalpha << true << noboolalpha << true << endl;
(3)指定整型值的进制:hex/oct/dec
指定整型值按十六进制/八进制/十进制输入输出,不影响浮点数
cout << hex << 10 << oct << 10 << dec << 10 << endl;
cin >> hex >> number;
(4)在输出中指出进制:使用showbase/noshowbase
使得输出时可以看出数字进制,前导0x
->hex
,前导0
->oct
cout << showbase << hex << 20 << noshowbase << endl;
注意:十六进制可以用uppercase/nouppercase
来切换大小写0X1FFF/0x1fff
(5)控制浮点数格式
- 以多高精度打印浮点值:默认六位数字精度
- 打印形式是十六进制、定点十进制还是科学记数法:自动选择可读性好的来打印
- 对于没有小数部分的浮点值是否打印小数点:默认不打印
(6)指定打印精度
在头文件iomanip中定义了setprecision
和其他接受参数的操纵符,可以通过调用IO对象的precision
成员或用setprecision
操纵符来改变精度
cout << cout.precision(2) << 3.1415 << setprecision(3) << 3.1415 << endl;
注意:只使用setprecision
是指定一共有效位数,而在使用scientific/fixed/hexfloat
后指定精度时就变成指定小数点后位数而不是一共位数
(7)指定浮点数记数法:fixed/scientific/hexfloat/defaultfloat
,定点十进制/科学记数法/十六进制/默认浮点格式
(8)打印小数点:showpoint/noshowpoint
(9)输出补白:setw()
指定一共输出位数,left/right
左/右对齐,internal
在符号和数字中间填充,setfill()
指定填充的字符来代替空白填充
注意:setw()
只影响下一个输出而不会影响流的状态
2、未格式化的输入/输出操作,参考《C++primer》p.673
(1)单字节操作
(2)将字符放回输入流
(3)多字节操作
注意:尽可能避免采用底层IO操作,容易出错
3、流随机访问,见《C++primer》p.676
标准库提供了一对函数来定位seek
到流中给定的位置,以及返回tell
我们当前的位置
(1)seek和tell函数
(2)只有一个标记
(3)重定位标记
(4)访问标记
(5)读写同一个文件
注意:随机IO本质上是依赖于系统的,且istream/ostream
并不支持随机IO,注意查阅具体资料