第十七章 标准库特殊设施
2020年11月10日大约 15 分钟
第十七章 标准库特殊设施
tuple类型
tuple是类似pair的模板,每个成员类型都可以不同,但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。tuple的类型从初始值的类型推断。 |
t1 == t2 | 当两个tuple具有相同数量的成员且成员对应相等时,两个tuple相等。 |
t1 relop t2 | tuple的关系运算使用字典序。两个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 | 一个类模板,可以通过一个整型常量和一个tuple类型来初始化。它有一个名为type的public成员,表示给定tuple类型中指定成员的类型。 |
定义和初始化tuple
定义和初始化示例:
tuple<size_t, size_t, size_t> threeD;tuple<size_t, size_t, size_t> threeD{1,2,3};auto item = make_tuple("0-999-78345-X", 3, 2.00);
访问tuple成员:
auto book = get<0>(item);get<2>(item) *= 0.8;
使用tuple返回多个值
tuple最常见的用途是从一个函数返回多个值。
bitset类型
- 处理二进制位的有序集;
bitset也是类模板,但尖括号中输入的是bitset的长度而不是元素类型,因为元素类型是固定的,都是一个二进制位。
初始化bitset的方法:
| 操作 | 解释 |
|---|---|
bitset<n> b; | b有n位;每一位均是0.此构造函数是一个constexpr。 |
bitset<n> b(u); | b是unsigned long long值u的低n位的拷贝。如果n大于unsigned long long的大小,则b中超出unsigned long long的高位被置为0。此构造函数是一个constexpr。 |
bitset<n> b(s, pos, m, zero, one); | b是string s从位置pos开始m个字符的拷贝。s只能包含字符zero或one:如果s包含任何其他字符,构造函数会抛出invalid_argument异常。字符在b中分别保存为zero和one。pos默认为0,m默认为string::npos,zero默认为'0',one默认为'1'。 |
bitset<n> b(cp, pos, m, zero, one); | 和上一个构造函数相同,但从cp指向的字符数组中拷贝字符。如果未提供m,则cp必须指向一个C风格字符串。如果提供了m,则从cp开始必须至少有m个zero或one字符。 |
初始化案例;
bitset<13> bitvec1(0xbeef);bitset<32> bitvec4("1100");
bitset操作:
| 操作 | 解释 |
|---|---|
b.any() | b中是否存在1。 |
b.all() | b中都是1。 |
b.none() | b中是否没有1。 |
b.count() | b中1的个数。 |
b.size() | |
b.test(pos) | pos下标是否是1 |
b.set(pos) | pos置1 |
b.set() | 所有都置1 |
b.reset(pos) | 将位置pos处的位复位 |
b.reset() | 将b中所有位复位 |
b.flip(pos) | 将位置pos处的位取反 |
b.flip() | 将b中所有位取反 |
b[pos] | 访问b中位置pos处的位;如果b是const的,则当该位置位时,返回true;否则返回false。 |
b.to_ulong() | 返回一个unsigned long值,其位模式和b相同。如果b中位模式不能放入指定的结果类型,则抛出一个overflow_error异常。 |
b.to_ullong() | 类似上面,返回一个unsigned long long值。 |
b.to_string(zero, one) | 返回一个string,表示b中位模式。zero和one默认为0和1。 |
os << b | 将b中二进制位打印为字符1或0,打印到流os。 |
is >> b | 从is读取字符存入b。当下一个字符不是1或0时,或是已经读入b.size()个位时,读取过程停止。 |
正则表达式
- 正则表达式(reqular expression)是一种描述字符序列的方法,是一种很强大的工具。
正则表达式库组件:
| 组件 | 解释 |
|---|---|
regex | 表示一个正则表达式的类 |
regex_match | 将一个字符序列与一个正则表达式匹配 |
regex_search | 寻找第一个与正则表达式匹配的子序列 |
regex_replace | 使用给定格式替换一个正则表达式 |
sregex_iterator | 迭代器适配器,调用regex_searcg来遍历一个string中所有匹配的子串 |
smatch | 容器类,保存在string中搜索的结果 |
ssub_match | string中匹配的子表达式的结果 |
regex_match和regex_search的参数:
| 操作 | 解释 |
|---|---|
(seq, m, r, mft) | 在字符序列seq中查找regex对象r中的正则表达式。seq可以是一个string、标识范围的一对迭代器、一个指向空字符结尾的字符数组的指针。 |
(seq, r, mft) | m是一个match对象,用来保存匹配结果的相关细节。m和seq必须具有兼容的类型。mft是一个可选的regex_constants::match_flag_type值。 |
- 这些操作会返回
bool值,指出是否找到匹配。
使用正则表达式库
regex使用的正则表达式语言是ECMAScript,模式[::alpha::](::alpha::)匹配任意字母。- 由于反斜线是C++中的特殊字符,在模式中每次出现
\的地方,必须用一个额外的反斜线\\告知C++我们需要一个反斜线字符。 - 简单案例:
string pattern("[^1]ei"); pattern = "[:alpha:](:alpha:)*" + pattern + "[:alpha:](:alpha:)*"查找不在字符c之后的字符串eiregex r(pattern);构造一个用于查找模式的regexsmatch results;定义一个对象保存搜索结果string test_str = "receipt freind theif receive";if (regex_search(test_str, results, r)) cout << results.str() << endl;如有匹配子串,打印匹配的单词。
regex(和wregex)选项:
| 操作 | 解释 |
|---|---|
regex r(re) regex r(re, f) | re表示一个正则表达式,它可以是一个string、一对表示字符范围的迭代器、一个指向空字符结尾的字符数组的指针、一个字符指针和一个计数器、一个花括号包围的字符列表。f是指出对象如何处理的标志。f通过下面列出来的值来设置。如果未指定f,其默认值为ECMAScript。 |
r1 = re | 将r1中的正则表达式替换Wiere。re表示一个正则表达式,它可以是另一个regex对象、一个string、一个指向空字符结尾的字符数组的指针或是一个花括号包围的字符列表。 |
r1.assign(re, f) | 和使用赋值运算符(=)的效果相同:可选的标志f也和regex的构造函数中对应的参数含义相同。 |
r.mark_count() | r中子表达式的数目 |
r.flags() | 返回r的标志集 |
定义regex时指定的标志:
| 操作 | 解释 |
|---|---|
icase | 在匹配过程中忽略大小写 |
nosubs | 不保存匹配的子表达式 |
optimize | 执行速度优先于构造速度 |
ECMAScript | 使用ECMA-262指定的语法 |
basic | 使用POSIX基本的正则表达式语法 |
extended | 使用POSIX扩展的正则表达式语法 |
awk | 使用POSIX版本的awk语言的语法 |
grep | 使用POSIX版本的grep的语法 |
egrep | 使用POSIX版本的egrep的语法 |
- 可以将正则表达式本身看做是一种简单程序语言设计的程序。在运行时,当一个
regex对象被初始化或被赋予新模式时,才被“编译”。 - 如果编写的正则表达式存在错误,会在运行时抛出一个
regex_error的异常。 - 避免创建不必要的正则表达式。构建一个
regex对象可能比较耗时。
匹配与regex迭代器类型
sregex_iterator操作(用来获得所有匹配):
| 操作 | 解释 |
|---|---|
sregex_iterator it(b, e, r); | 一个sregex_iterator,遍历迭代器b和e表示的string。它调用sregex_search(b, e, r)将it定位到输入中第一个匹配的位置。 |
sregex_iterator end; | sregex_iterator的尾后迭代器 |
*it, it-> | 根据最后一个调用regex_search的结果,返回一个smatch对象的引用或一个指向smatch对象的指针。 |
++it , it++ | 从输入序列当前匹配位置开始调用regex_search。前置版本返回递增后迭代器;后置版本返回旧值。 |
it1 == it2 | 如果两个sregex_iterator都是尾后迭代器,则它们相等。两个非尾后迭代器是从相同的输入序列和regex对象构造,则它们相等。 |
示例:
// 将字符串file中所有匹配模式r的子串输出
for (sregex_iterator it(file.begin(), file.end(), r), end_it; it != end_it; ++it){
cout << it ->str() << endl;
}smatch操作:
| 操作 | 解释 |
|---|---|
m.ready() | 如果已经通过调用regex_search或regex_match设置了m,则返回true;否则返回false。如果ready返回false,则对m进行操作是未定义的。 |
m.size() | 如果匹配失败,则返回0,;否则返回最近一次匹配的正则表达式中子表达式的数目。 |
m.empty() | 等价于m.size() == 0 |
m.prefix() | 一个ssub_match对象,标识当前匹配之前的序列 |
m.suffix() | 一个ssub_match对象,标识当前匹配之后的部分 |
m.format(…) | |
m.length(n) | 第n个匹配的子表达式的大小 |
m.position(n) | 第n个子表达式距离序列开始的长度 |
m.str(n) | 第n个子表达式匹配的string |
m[n] | 对应第n个子表达式的ssub_match对象 |
m.begin(), m.end() | 表示m中ssub_match元素范围的迭代器。 |
m.cbegin(), m.cend() | 常量迭代器 |
使用子表达式
- 正则表达式语法通常用括号表示子表达式。
- 子表达式的索引从1开始。
- 在
fmt中用$后跟子表达式的索引号来标识一个特定的子表达式。
示例:
if (regex_search(filename, results, r))
cout << results.str(1) << endl; // .str(1)获取第一个子表达式匹配结果ssub_match子匹配操作:
| 操作 | 解释 |
|---|---|
matched | 一个public bool数据成员,指出ssub_match是否匹配了 |
first, second | public数据成员,指向匹配序列首元素和尾后位置的迭代器。如果未匹配,则first和second是相等的。 |
length() | 匹配的大小,如果matched为false,则返回0。 |
str() | 返回一个包含输入中匹配部分的string。如果matched为false,则返回空string。 |
s = ssub | 将ssub_match对象ssub转化为string对象s。等价于s=ssub.str(),转换运算符不是explicit的。 |
使用regex_replace
正则表达式替换操作:
| 操作 | 解释 |
|---|---|
m.format(dest, fmt, mft), m.format(fmt, mft) | 使用格式字符串fmt生成格式化输出,匹配在m中,可选的match_flag_type标志在mft中。第一个版本写入迭代器dest指向的目的为止,并接受fmt参数,可以是一个string,也可以是一个指向空字符结尾的字符数组的指针。mft的默认值是format_default。 |
rege_replace(dest, seq, r, fmt, mft), regex_replace(seq, r, fmt, mft) | 遍历seq,用regex_search查找与regex对象r相匹配的子串,使用格式字符串fmt和可选的match_flag_type标志来生成输出。mft的默认值是match_default |
示例:
string phone = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ]?)(\\d{4})"
string fmt = "$2.$5.$7"; // 将号码格式改为ddd.ddd.dddd
regex r(phone); // 用来寻找模式的regex对象
string number = "(908) 555-1800";
cout << regex_replace(number, r, fmt) << endl;匹配标志:
| 操作 | 解释 |
|---|---|
match_default | 等价于format_default |
match_not_bol | 不将首字符作为行首处理 |
match_not_eol | 不将尾字符作为行尾处理 |
match_not_bow | 不将首字符作为单词首处理 |
match_not_eow | 不将尾字符作为单词尾处理 |
match_any | 如果存在多于一个匹配,则可以返回任意一个匹配 |
match_not_null | 不匹配任何空序列 |
match_continuous | 匹配必须从输入的首字符开始 |
match_prev_avail | 输入序列包含第一个匹配之前的内容 |
format_default | 用ECMAScript规则替换字符串 |
format_sed | 用POSIX sed规则替换字符串 |
format_no_copy | 不输出输入序列中未匹配的部分 |
format_first_only | 只替换子表达式的第一次出现 |
随机数
- 新标准之前,C和C++都依赖一个简单的C库函数
rand来生成随机数,且只符合均匀分布。 - 新标准:随机数引擎 + 随机数分布类, 定义在
random头文件中。 - C++程序应该使用
default_random_engine类和恰当的分布类对象。
随机数引擎和分布
随机数引擎操作
| 操作 | 解释 |
|---|---|
Engine e; | 默认构造函数;使用该引擎类型默认的种子 |
Engine e(s); | 使用整型值s作为种子 |
e.seed(s) | 使用种子s重置引擎的状态 |
e.min(),e.max() | 此引擎可生成的最小值和最大值 |
Engine::result_type | 此引擎生成的unsigned整型类型 |
e.discard(u) | 将引擎推进u步;u的类型为unsigned long long |
示例:
// 初始化分布类型
uniform_int_distribution<unsigned> u(0, 9);
// 初始化引擎
default_random_engine e;
// 随机生成0-9的无符号整数
cout << u(e) << endl;设置随机数发生器种子:
- 种子就是一个数值,引擎可以利用它从序列中一个新位置重新开始生成随机数。
- 种子可以使用系统函数
time(0)。
其他随机数分布
分布类型的操作:
| 操作 | 解释 |
|---|---|
Dist d; | 默认够赞函数;使d准备好被使用。其他构造函数依赖于Dist的类型;分布类型的构造函数是explicit的。 |
d(e) | 用相同的e连续调用d的话,会根据d的分布式类型生成一个随机数序列;e是一个随机数引擎对象。 |
d.min(),d.max() | 返回d(e)能生成的最小值和最大值。 |
d.reset() | 重建d的状态,是的随后对d的使用不依赖于d已经生成的值。 |
IO库再探
格式化输入与输出
- 使用操纵符改变格式状态。
- 控制布尔值的格式:
cout << boolalpha << true << endl; - 指定整型的进制:
cout << dec << 20 << endl;
定义在iostream中的操纵符:
| 操纵符 | 解释 |
|---|---|
boolalpha | 将true和false输出为字符串 |
* noboolalpha | 将true和false输出为1,0 |
showbase | 对整型值输出表示进制的前缀 |
* noshowbase | 不生成表示进制的前缀 |
showpoint | 对浮点值总是显示小数点 |
* noshowpoint | 只有当浮点值包含小数部分时才显示小数点 |
showpos | 对非负数显示+ |
* noshowpos | 对非负数不显示+ |
uppercase | 在十六进制中打印0X,在科学计数法中打印E |
* nouppercase | 在十六进制中打印0x,在科学计数法中打印e |
* dec | 整型值显示为十进制 |
hex | 整型值显示为十六进制 |
oct | 整型值显示为八进制 |
left | 在值的右侧添加填充字符 |
right | 在值的左侧添加填充字符 |
internal | 在符号和值之间添加填充字符 |
fixed | 浮点值显示为定点十进制 |
scientific | 浮点值显示为科学计数法 |
hexfloat | 浮点值显示为十六进制(C++11) |
defaultfloat | 充值浮点数格式为十进制(C++11) |
unitbuf | 每次输出操作后都刷新缓冲区 |
| 1 | * nounitbuf |
* skipws | 输入运算符跳过空白符 |
noskipws | 输入运算符不跳过空白符 |
flush | 刷新ostream缓冲区 |
ends | 插入空字符,然后刷新ostream缓冲区 |
endl | 插入换行,然后刷新ostream缓冲区 |
其中*表示默认的流状态。
未格式化的输入/输出操作
单字节低层IO操作:
| 操作 | 解释 |
|---|---|
is.get(ch) | 从istream is读取下一个字节存入字符cn中。返回is。 |
os.put(ch) | 将字符ch输出到ostream os。返回os。 |
is.get() | 将is的下一个字节作为int返回 |
is.putback(ch) | 将字符ch放回is。返回is。 |
is.unget() | 将is向后移动一个字节。返回is。 |
is.peek() | 将下一个字节作为int返回,但不从流中删除它。 |
多字节低层IO操作:
| 操作 | 解释 |
|---|---|
is.get(sink, size, delim) | 从is中读取最多size个字节,并保存在字符数组中,字符数组的起始地址由sink给出。读取过程直到遇到字符delim或读取了size个字节或遇到文件尾时停止。如果遇到了delim,则将其留在输入流中,不读取出来存入sink。 |
is.getline(sink, size, delim) | 与接收三个参数的get版本类似,但会读取并丢弃delim。 |
is.read(sink, size) | 读取最多size个字节,存入字符数组sink中。返回is。 |
is.gcount() | 返回上一个未格式化读取从is读取的字节数 |
os.write(source, size) | 将字符数组source中的size个字节写入os。返回os。 |
is.ignore(size, delim) | 读取并忽略最多size个字符,包括delim。与其他未格式化函数不同,ignore有默认参数:size默认值是1,delim的默认值为文件尾。 |
- 注意:一般情况下,主张使用标准库提供的高层抽象,低层函数容易出错。
流随机访问
- 只适用于
fstream和sstream。 - 通过将标记
seek到一个给定位置来重定位它。 tell告诉我们标记的当前位置。
| 操作 | 解释 |
|---|---|
tellg(),tellp | 返回一个输入流中(tellg)或输出流中(tellp)标记的当前位置。 |
seekg(pos),seekp(pos) | 在一个输入流或输出流中将标记重定位到给定的绝对地址。pos通常是一个当前teelg或tellp返回的值。 |
seekp(off, from),seekg(off, from) | 在一个输入流或输出流中将标记定位到from之前或之后off个字符,from可以是下列值之一:beg,偏移量相对于流开始位置;cur,偏移量相对于流当前位置;end,偏移量相对于流结尾位置。 |