C++ 小白 学习记录17

第十七章  标准库特殊设施

17.1 tuple类型

tuple类似于大杂烩, 什么都可以往里装.

可以用于打包动作

tuple<T1, T2, ..., Tn> t;

tuple<T1, T2, ..., Tn> t(v1, v2..., vn);

make_tuple(v1, v2, ...,vn)

t1 == t2  数量相同, 对应的成员也相等

t1 != t2

t1 relop t2  关系运算使用字典序, 两个tuple必须具有相同数量的成员, 使用<运算符比较成员

get<i>(t) 返回第i个数据成员的引用

tuple_size<tupleType>::value  表示tuple类型中成员的数量, 即 有多少列, 从零开始.

tuple_element<i, tupleType>::type  表示tuple类型中指定成员的类型 用于声明变量.

	tuple<string, vector<double>, int, list<int>> item("constants", { 3.14,2.71 }, 42, { 0, 1, 2,3 });
	auto str = std::get<0>(item);  // string
	typedef decltype(item) itemType; // itemType = tuple<string, vector<double>, int, list<int>>
	size_t sz = tuple_size<itemType>::value; // 4 列
	tuple_element<1, itemType>::type cnt = get<1>(item); //  vector<double> cnt = {3.14, 2.71}

17.1.1 定义和初始化tuple

定义时需要指出其每个成员的类型

17.1.2 使用tuple返回多个值

17.2 bitset类型

bitset<n> b   b有n位, 每一位都是0

bitset<n>b(u)  b是unsigned long long 值u的低n位的拷贝. 如果n大于u的长度, 则高位被置为0

bitset<n> b(s, pos, m, zero, one); b是string s 从pos开始m个字符的拷贝. s只能包含字符zero或one. 否则会抛出异常. pos默认0, m默认string::npos, zero默认'0', one 默认'1'

bitset<n> b(cp, pos, m, zero, one); cp指向数组.

  • 使用unsigned值初始化bitset
  • 该unsigned 会被转换为unsigned long long 类型
  • bitset中的二进制位是该unsigned值的副本
  • bitset的长度大于unsigned long long的长度时, 剩余的高位均被置为0
  • bitset的长度小于unsigned long long的长度时, 剩余的高位被舍弃

string 初始化 bitset:

字符串中下标最小的字符对应高位:

bitset<32>bitvec("1100");  // 即: 1100

string中的下标 是从左开始, bitset中的下标 是从右开始的,其遵照二进制的顺序

  • b.any ()        b中是否存在1的二进制位
  • b.all()           b中全部都是1的吗?
  • b.none()      b中没有1的吗?
  • b.count()     b中1个个数
  • b.size()       b的长度
  • b.test(pos) b中pos的位置是否为1
  • b.set(pos, v) 将pos位置置为bool类型v的值, v默认为true
  • b.set()  所有位置置为为1
  • b.reset() 所有位置置为为0
  • b.reset(pos) pos位置置为0
  • b.flip(pos) 改变pos位置的状态
  • b.flip() 所有位置改变状态
  • b[pos]  访问pos位
  • b.to_ulong()
  • b.to_ullong()
  • b.to_string(zero,one)
  • os<<b
  • is>>b

17.3 正则表达式

头文件<regex>

组件:

  • regex   表示有一个正则表达式的类
  • regex_match  将一个字符序列与一个正则表达式匹配
  • regex_search  寻找第一个与正则表达式匹配的子序列
  • regex_replace  使用给定格式替换一个正则表达式
  • sregex_iterator  迭代器适配器, 调用regex_search来遍历一个string中所有匹配的子串
  • smatch  容器类, 保存string中搜索的结果
  • ssub_match  string中匹配的子表达式的结果

regex_search和regex_match的参数:

  • (seq, m, r, mft)  在字符序列seq中查找regex对象r中的正则表达式.
  •     seq 可以是string, 一对迭代器, 指向空字符结尾的字符数组的指针
  •     m是一个match对象, 用来保存匹配结果的相关细节, m和seq必须具有兼容的类型
  •     mft 是一个可选的regex_constants::match_flag_type值
  • (seq, r, mft)

17.3.1 使用正则表达式库

regex的一些配置:

  • regex r(re)    re表示一个正则表达式, 可以是string/表示字符的迭代器对/指向空字符结尾的字符数组/字符指针和一个计数器/字符列表.  f 指出对象如何处理的标志, f默认为ECMAScript.
  • regex r(re, f)
  • r1 = re  将r1的正则表达式替换为re, re可以是 正则表达式/string/空字符结尾的数组指针/字符列表
  • r1.assign(re, f)
  • 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语法
	// 查找不在字符c之后的字符串ei
	string pattern("[^c]ei");
	pattern = "[[:alpha:]]*" + pattern + "[[:alpha:]]*";
	// '/\w*[^c]ei\w*/'  通常所使用的正则表达式
    // 等同于 string pattern("\\w*[^c]ei\\w*");
	regex r(pattern);
	smatch results;
	string test_str = "receipt freind theif receive";
	if (regex_search(test_str, results, r)) cout << results.str() << endl;

regeex_error异常,  错误的正则表达式抛出的异常. what 中有描述, code有错误编码:

搜索结果xmatch与搜索类型的匹配:

  • 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

17.3.2 匹配与Regex迭代器类型

  • sregex_iterator it(b, e, r) 一个sregex_iterator 遍历迭代器b和e所表示的string, 其调用sregex_search(b, e, r) 将it定位到输入中第一个匹配的位置.
  • sregex_iterator end;  尾后迭代器. 只需要定义, 不需要赋值, 如实例.
  • *it
  • it->
  • ++it
  • it++
  • it1 == it2
  • it1 != it2
    string pattern("\\w*[^c]ei\\w*");
	regex r(pattern);
	smatch results;
	string test_str = "receipt freind theif receive";
	sregex_iterator it(test_str.begin(), test_str.end(), r);
	sregex_iterator  it_end;  // 只是定义了一下, 并未做处理
	for (; it != it_end; ++it) {
		auto pos = it->prefix().length();
		pos = pos > 40 ? pos - 40 : 0;

		cout << it->prefix().str().substr(pos)
			<< "\n\t\t>>> " << it->str() << " <<<\n"
			<< it->suffix().str().substr(0,40)
			<< endl;
	}

prefix,  suffix 获取匹配处前/后缀的字符串. 其余的操作还有

  • m.ready()  如果已经通过调用regex_serach或regex_match, 则返回true, 否则返回false, 如果是false, 则m的操作都是未定义的
  • m.size()  匹配失败 返回0, 否则返回最近一次匹配的子表达式的数目
  • m.empty()  return (m.size() ==0)? true: false
  • m.suffix() 一个ssub_match对象, 表示匹配之后的部分
  • m.prefix() 一个ssub_match对象, 表示匹配之前的部分
  • m.format(...)  
  • m.length(n)  第n个匹配的子表达式的大小
  • m.position(n)  第n个匹配的子表达式距序列开始的距离
  • m.str(n)  第n个匹配的子表达式匹配的string, 正则表达式中第n个子表达式匹配的内容
  • m[n]  第n个匹配的子表达式的ssub_match对象
  • m.begin(), m.end()  表示m中sub_match元素范围的迭代器.
  • m.cbegin(), m.cend()

17.3.3 使用子表达式

第一个子匹配位置为0  表示整个模式对应的匹配, 随后是每个子表达式对应的匹配.

子匹配的一些操作:

  • matched  指出ssub_match 是否匹配
  • first  指向匹配序列首元素和尾后位置的迭代器, 如果未匹配, first == second
  • second
  • length()  匹配的大小
  • str()  返回一个包含输入中匹配部分的string, 如果matched为false, 则返回空string
  • s = ssub   等价于 s = ssub.str()

17.3.4 使用regex_replace

regex_replace(dest, r, fmt, mft)

dest  需要替换字符串的原始字符串, 函数执行后 该字符串会被修改

r  正则表达式对象

fmt  一个字符串, 其中的$ 代表子表达式匹配的内容, 需要为子表达式的序号

mft 表示一些匹配控制标志, 这些标志均在std::regex_constants::match_flag_type中.

	string phone = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ]?)(\\d{4})";
	regex rphone(phone);
	string dest = "(908) 555-1888";
	string fmt = "$2.$5.$7";  // 使用第2, 5, 7个子表达式匹配的内容组成新内容
	cout << regex_replace(dest, rphone, fmt) << endl; // 输出 908.555.1888

 

17.4 随机数

<random>

default_random_engine 随机数引擎类:  类型 生成随机unsigned整数序列

随机数分布类:  类型, 使用引擎返回服从特定概率分布的随机数

17.4.1 随机数引擎和分布

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
#include<random>
using namespace std;
int main(int argc, char** argv) {
	uniform_int_distribution<unsigned> u(0, 9); // 分布类型生成均匀分布的unsigned值
	default_random_engine e;
	for (size_t i = 0; i < 10; ++i)
		cout << u(e) << " - "; // Engine e(s) 生成 2 - 2 - 4 - 5 - 4 - 1 - 9 - 5 - 8 - 3 -
// 分布类型接收一个随机数引擎作为参数, 并使用它生成随机数, 然后映射到指定的分布
}

注意: 传递给分布对象的是引擎对象, 即 u(e) 而不是u(e()).  传递的是引擎本身, 而不是它生成的下一个值(e()), 因为某些分布可能需要调用引擎多次才能得到一个值.

随机数发生器: 指 分布对象和引擎对象的组合

比较随机数引擎和rand函数:

default_random_engine  生成的随机数范围为unsigned整数, e.min(), e.max()可得到该范围

random生成的数在0 到 RAND_MAX之间.

引擎生成一个数值序列:

 即使生成的数看起来是随机的, 但对一个给定的发生器, 每次运行程序它返回相同的数值序列.

这个有点超出想象了

vector<unsigned> bad_randVec() {
	// 每次生成的对象 都会随机出相同的 随机数序列
	default_random_engine e;
	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;
}
vector<unsigned> good_randVec() {
	// 使用static后 每次调用都使用相同的对象, 对象的状态得以保存, 会继续向后随机
	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;
}

int main(int argc, char** argv) {
	vector<unsigned> v1(bad_randVec());
	vector<unsigned> v2(bad_randVec());
	cout << "equal? " << (v1 == v2) << endl; // v1 和v2 相等

	vector<unsigned> v3(good_randVec());
	vector<unsigned> v4(good_randVec());
	cout << "equal? " << (v3 == v4) << endl; // v3 和 v4 不相等
}

设置随机数发生器种子:

种子是一个数值, 引擎可以利用它从序列中一个新位置重新开始生成随机数.

有两种方式: 创建时提供种子,  使用引擎的seed成员

种子相同, 引擎生成的序列相同.

对于秒间隔以上的可以使用time(0) 作为种子, 其余情况需要重新想办法

default_random_engine e(time(0));

17.4.2 其他随机数分布

生成随机实数: 随机浮点数

注意使用rand()/RAND_MAX的方法是不可取的, 因为精度低.有些浮点值永远不会被生成.

	default_random_engine e;
	uniform_real_distribution<double> u(0, 1);
	for (size_t i = 0; i < 10; ++i) cout << u(e) << endl;

生成非均匀分布的随机数:

一个正态分布例子:

	// 生成一组200个正态分布的点, 值以4为中心, 标准差1.5, 99%在[0, 8]
	default_random_engine e;
	normal_distribution<>n(4, 1.5);  // 正态分布
	vector<unsigned> vals(9);
	for (size_t i = 0; i != 200; ++i) {
		unsigned v = lround(n(e));
		if (v < vals.size()) ++vals[v];
	}
	size_t total = 0;
	for (size_t j = 0; j != vals.size(); ++j)
	{
		cout << j << ": " << string(vals[j], '*') << endl;
		total += vals[j];
	}
	cout << "total: " << total << endl;

一个返回true/false的随机分布, 返回true的概率总是50%:

	string resp;
	default_random_engine e;
	bernoulli_distribution b; // 可以定义概率b(.55) 即 55:45 55为true
	do {
		bool first = b(e);
		cout << (first ? "We go first" : "You get to go first") << endl;
		cout << "play something...." << endl;
		cout << "play again ? Enter 'yes' or 'no'" << endl;
	} while (cin >> resp && resp[0] == 'y');

17.5 IO库

17.5.1 格式化输入和输出

操纵符用于两大类输出控制: 控制数值的输出形式以及控制补白的数量和位置. 大多数操纵符成对出现, 一个用于设置, 一个用于复原. 一旦设置 后续流则全部被改变.

boolalpha 更改true/ false的默认输出值, 不再是1, 0, 而是 true false

noboolalpha 取消

hex, oct, dec 改变进制输出

uppercase/nouppercase 可以改变16进制中0x, 为大写 0X

showbase/noshowbase 可以显示进制信息

	cout << showbase;
	cout << "default: 20: " << 20 << "  1024: " << 1024 << endl;
	cout << "in octal: 20: " <<oct << 20 << "  1024: " << 1024 << endl;
	cout << "in hex: 20: " << hex << 20 << "  1024: " << 1024 << endl;
	cout << "in decimal: 20: " << dec << 20 << "  1024: " << 1024 << endl;
	cout << noshowbase;
	/*default: 20 : 20  1024 : 1024
	in octal : 20 : 024  1024 : 02000
	in hex : 20 : 0x14  1024 : 0x400
	in decimal : 20 : 20  1024 : 1024*/

控制浮点数输出的三种格式:

  • 以多高精度打印浮点值
  • 数值打印为十六进制, 定点十进制还是科学记数法形式
  • 没有小数部分的浮点值是否打印小数点

cout.precision() 返回当前打印的精度值

cout.precision(n) 设置打印精度为n位数字

  • boolalpha    将true/false 输出为字符串 而非 1/0
  • noboolalpha
  • showbase    输出整型数字的前缀 比如 0x 十六进制, 0 八进制
  • noshowbase
  • showpoint    对浮点值总是显示小数点 即使是没有小数部分
  • noshowpoint
  • showpos    非负数前面显示+号
  • noshowpos
  • uppercase    十六进制中的0x大写为0X, 科学记数中的e变为E
  • nouppercase
  • dec    整数显示为十进制
  • hex    整数显示为十六进制
  • oct    整数显示为八进制
  • left    在值的右侧添加填充字符  左对齐输出
  • right    在值的左侧添加填充字符  右对齐输出
  • internal    在符号和值之间添加填充字符
  • fixed    浮点值显示为定点十进制
  • scientific    浮点值显示为科学记数法
  • hexfloat    浮点值显示为十六进制
  • defaultfloat    重置浮点数格式为十进制
  • unitbuf    每次输出操作后都刷新缓冲区
  • nounitbuf
  • skipws    出入运算符跳过空白符
  • noskipws
  • flush    刷新ostream缓冲区
  • ends    插入空字符, 然后刷新ostream缓冲区
  • endl    插入换行, 然后刷新ostream缓冲区
	#include <iomanip>
    int i = -16;
	double d = 3.1415926;
	cout << left
		<<"i: " << setw(12) << i <<"    next col" << '\n'
		<<"d: " << setw(12) << d<< "    next col" <<
		endl;
	cout << right
		<< "i: " << setw(12) << i << "    next col" << '\n'
		<< "d: " << setw(12) << d << "    next col" <<
		endl;
	cout << internal
		<< "i: " << setw(12) << i << "    next col" << '\n'
		<< "d: " << setw(12) << d << "    next col" <<
		endl;
	cout << setfill('#')
		<< "i: " << setw(12) << i << "    next col" << '\n'
		<< "d: " << setw(12) << d << "    next col" <<
		endl;

	//i: -16              next col
	//d : 3.14159         next col
	//i :          -16    next col
	//d :      3.14159    next col
	//i: -          16    next col
	//d :      3.14159    next col
	//i : -#########16    next col
	//d : #####3.14159    next col

17.5.2 未格式化的输入/输出操作

单字节操作:

  • is.get(ch)    从istream is 读取下一个字节存入字符ch中, 返回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返回, 但不从流中删除它
	int ch;  //使用int 而非char保存get的返回值
	while ((ch = cin.get()) != EOF) cout.put(ch);

多字节操作:

  • is.get(sink, size, delim)    从is中读取最多size个字节, 并保存在字符数组中, 字符数组的起始地址有sink确定, 读取过程直至遇到字符delim或读取了size个字节或者遇到文件尾时停止. delim 仍然保留在流中, 不会被保存到sink中.  sink 为 char数组
  • is.getline(sink, size, delim)  同get版本, 但是会丢弃delim
  • is.read(sink, size)    读取size个字节, 存入数组sink中, 返回is
  • is.gcount()    返回上一个未格式化读取操作 从is读取的字节数, 应该在peek, unget, puback之前调用, 否则gcount() 返回值为 0
  • os.write(source, size)    将字符数组source中的size个字节写入到os, 返回os
  • is.ignore(size, delim)    读取并忽略最多size个字节, 包括delim. size 默认为1, delim 默认EOF

17.5.3 流随机访问

seek  定位流位置

tell 报告当前位置

两个函数依赖于设备的系统. istream/ ostream通常不支持, 仅fstream/sstream支持.

  • tellg()    返回输入流中(tellg)或输出流中(tellp) 标记的当前位置
  • tellp()
  • seekg(pos)    在一个输入流(seekg)或输出流(seekp)中 将标记重定位到给定的绝对位置. pos通常是前一个tellg或tellp返回的值.
  • seekp(pos)
  • seekp(off, from)    在一个输入流(seekp)或输出流(seekg)中将标记定位到from之前或之后 off个字符, from: beg  偏移量相对于流开始的位置,  cur 偏移量相对于流当前位置, end 偏移量相对于流结尾的位置
  • seekg(off, from)

istream和派生自istream的类型 ifstream, istringstream  使用g版本

ostream和派生自ostream的 ofstream, ostringstream 使用p版本

iostream, fstream, stringstream ,  两者都可用

因为只有单一的标记, 所以只要在读写操作间切换, 就必须进行seek操作重新定位标记.

tellg tellp 返回一个机器相关的位置值 ostringstream::pos_type mark = .

// in, out 以读写方式打开文件, ate, 打开后移动到文件结尾
	fstream inOut("E:\\c++\\primer\\Project_17\\Debug\\p678.txt", fstream::ate | fstream::in | fstream::out);
	if (!inOut) {
		cerr << "unable to open file!" << endl;
		return EXIT_FAILURE;
	}
	// 记住文件当前的位置
	auto end_mark = inOut.tellg();  
	// 移动游标(姑且这么认为)到文件开始
	inOut.seekg(0, fstream::beg);
	size_t cnt = 0;
	string line;
	// 读取一行
	while (inOut && inOut.tellg() != end_mark && getline(inOut, line)) {
		// 计算时 需要增加行尾 不可见字符
		cnt += line.size() + 1;
		// 记住当前位置 以便于下一个循环时返回到当前的位置继续向下读取
		auto mark = inOut.tellg();
		// 跳到文件尾 准备写 行号
		inOut.seekp(0, fstream::end);
		// 写行号
		inOut << cnt;
		if (mark != end_mark) inOut << " - ";
		// 跳到当初计算cnt的地方
		inOut.seekg(mark);
	}
	inOut.seekp(0, fstream::end);
	inOut << "\n";
	return 0;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yutao1131

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值