S17标准库特殊设施

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/1EOF或读满时读取结束并将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返回trueregex_search/regex_match的参数表为(seq, m, r, mft)(seq, r, mft)seq为待查找的输入序列,可以是string、表示范围的一对迭代器或字符数组的指针,m是一个match对象可以保存匹配的细节(mseq必须兼容),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)正则表达式类和输入序列类型

输入序列类型应使用的正则表达式类
stringregex/smatch/ssub_match/sregex_iterator
const char *regex/cmatch/csub_match/cregex_iterator
wstringwregex/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/falsenoboolalpha复原

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,注意查阅具体资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值