【C++ Primer】标准库特殊设施

一、tuple类型

  tuple是类似pair的模板。每个pair的成员类型都不同,但每个pair都恰好有两个成员。不同tuple类型的成员类型也不相同,但一个tuple可以有任意数量的成员。每个确定的tuple类型的成员数目是固定的,但一个tuple类型的成员数目可以与另一个tuple类型不同。
  当我们希望将一些数据组合成单一对象,但又不想麻烦地定义一个新数据结构来表示这些数据时,tuple是非常有用的。
tuple支持的操作:
这里写图片描述

#include <bits/stdc++.h>
using namespace std;

tuple<int, string> foo()//函数foo返回tuple类型。
{
	return make_tuple(2014, "tuple");//用make_tuple来构造一个tuple。 
}

int main(int argc, char const *argv[])
{

	/******************
	*定义和初始化tuple。
	*******************/

	tuple<string, vector<double>, pair<string, int>> item("abc", {1.0, 2.0, 3.0}, {"ABC", 3});
	//用get访问tuple数据成员,返回第三个成员pair类型的第一个元素。
	cout << get<2>(item).first << endl;	

	//访问tuple成员:
    const int a = 0; 
    int b[3];                        
  	auto item2 = make_tuple(a,b);// tuple < int, int* >
	cout << get<0>(item2) << endl;	

	//关系和相等运算符:
	//逐个比较元素,元素的数量和类型必须一致。
	tuple<size_t, size_t> item3_1(1,2);
	tuple<size_t, size_t> item3_2(2,1);
	cout << (item3_1 < item3_2) << endl;

	/********************
	*使用tuple返回多个值。
	*********************/
	
    int a2;  
    string b2;  
    //通过std::tie解包tuple。
 	tie(a2, b2) = foo(); 
 	cout << a2 << " " << b2 << endl;

	system("pause");
	return 0;
}

这里写图片描述

二、bitset类型

  初始化bitset的方法:
这里写图片描述

#include <bits/stdc++.h>
using namespace std;

//非类型模板参数。
template <unsigned N>
class bits
{
public:
	bits() = default;
	bits(const string &s) : bit(s) { }
	~bits() =default;
	inline void updata(int s, int b)
	{
		bit.set(s, b);
	}

	//为了让实例成为友元,友元声明中必须使用与类模板本身不同的模板参数。
    template<unsigned M>
    friend size_t text(const bits<M> &lb, const bits<M> &rb);  
  	//重载输出操作符。
    template<unsigned M>  
    friend std::ostream &operator<<(std::ostream &os, const bits<M> &b);  

private:
	bitset<N> bit;
};

template<unsigned M>
size_t text(const bits<M> &lb, const bits<M> &rb)
{
	auto temp = lb.bit ^ rb.bit;//使用亦或操作。
	return temp.count();//计算同一位置上数字不同的有多少个。
}

template<unsigned M>  
std::ostream &operator<<(std::ostream &os, const bits<M> &b)  
{  
    os << b.bit;  
    return os;  
}  

int main(int argc, char const *argv[])
{
	bitset<4> bit1(10);
	//比初始值大则高位补零,小则高位被丢弃。
	cout << bit1 << endl;

	/***********************
	*使用string初始化bitset。
	************************/
	
	//字符串下标最小的字符对应高位。
	string str("11010011");
	bitset<10> bit2_1(str);
	bitset<10> bit2_2(str, 1, 3);//从str[1]开始的三个字符。
	bitset<10> bit2_3(str, str.size()-2);//使用最后两个字符。
 	cout << bit2_1 << endl << bit2_2 << endl << bit2_3 << endl;

 	/***********
 	*bitset操作。
 	************/
 	int a = 100;
 	bitset<10> bit3(a);
 	cout << bit3 << endl;
 	//计算位数和置位的位数(1的个数)。
 	cout << bit3.size() << ", " << bit3.count() << endl;
 	bit3.set(0,1);//把最低位设为1。
 	cout << bit3 << endl;
 	cout << bit3.to_ulong() << endl;//提取bitset的值。

 	
	bits<10> bit4_1("1010101101");
	bits<10> bit4_2("1110001111");
	bit4_2.updata(0,0);//更新低位的值。
	cout << text(bit4_1, bit4_2) << endl;


	return 0;
}

这里写图片描述

  bitset的应用:
  (1)海量数据排序:
  某区有7位电话号码若干(千万级别),要求对其排序,占用内存要求在1MB左右。大概算一下,8位电话号码也就是0000000-9999999,数值一共不到1千万吧,用unsigned即可。那么加载进内存越为4*10000000字节,约等于40MB,如果我们用排序只能用归并排序了,至少也40次了吧,先不说次数多少,就磁盘IO来说就慢的不行了。
  此时可以考虑bitset了,既然电话号码是连续的且不重复的,我们可以用bitset的bit下标来表示电话号码,比如1234567,就是bit[1234567]。那么好办了,
  首先计算一下bitset需要的大概40MB/32,也就是1MB左右,复合情况,我们可以直接遍历一遍,存在的号码S我们设置b[S]位为1,不存在则为0。那么输出排序直接遍历bitset即可。节省空间且速度快。
  (2)海量数据找不重复数:
  2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。这次用两个位表示一个数,00表示未出现过,01表示出现了1次,11表示出现重复了。遍历即可。

三、 正则表达式

  正则表达式是一种描述字符序列的方法,是一种极其强大的计算工具。
  正则表达式库组件:
这里写图片描述
在这里插入图片描述

1、使用正则表达式库

  一个正则表达式的语法是否正确是在运行时解析的。

/*****************
*使用正则表达式库。
******************/

//[^]表示非。
string pattern1_1("[^c]ei");
//前后匹配任意大小写的零个或多个字母。
pattern1_1 = "[[:alpha:]]*" + pattern1_1 + "[[:alpha:]]*";
regex r1_1(pattern1_1);//创建用于查找模式的regex。
smatch result1_1;
string test_string1_1 = "receipt freid theif receive";
//寻找第一个与正则表达式匹配的子序列,找到就停止。
if(regex_search(test_string1_1, result1_1, r1_1))
	cout << result1_1.str() << endl;

/*
*$表示锚定行尾,只匹配$之前的内容。
*.表示匹配其后的任意字符。
*\\.表示去掉特殊含义,
*regex::icase指定regex对象的选项,表示在匹配过程中忽略大小写。
*正则表达式类型必须与输入类型匹配。smatch与string匹配,cmatch与char匹配。
*/
try
{
	regex r1_2("([[:alpha:]]*)(\\.)([[:alpha:]]*)", regex::icase);
	smatch result1_2;
	string test_string1_2 = "abc.cpp";
	if(regex_search(test_string1_2, result1_2, r1_2))
		cout << result1_2.str(3) << endl;//使用子表达式,打印第三个子表达式。
}
catch (regex_error e)
{
	//what()描述错误类型,code()表示错误编码。
	cout << e.what() << "\ncode: " << e.code() << endl;
}

2、匹配与regex迭代器类型

/**********************
*匹配与regex迭代器类型。
***********************/

string pattern2("[^c]ei");
pattern2 = "[[:alpha:]]*" + pattern2 + "[[:alpha:]]*";
regex r2(pattern2, regex::icase);//创建用于查找模式的regex。
string test_string2 = "receipt freid abcdef theif receive";
//sregex_iterator将迭代器定位到第一个匹配的位置。
for (sregex_iterator it2(test_string2.begin(), test_string2.end(), r2), 
						end_it; it2 != end_it; ++it2)
//打印出匹配单词的上下文。
{
	auto pos = it2->prefix().length();//前缀的大小。
	pos = pos > 10 ? pos - 6 : 0;//最多需要6个字符。
	cout << it2->prefix().str().substr(pos) << //前缀的最后一部分。
		 "\n\t\t>>>" << it2->str() << " <<<\n" << //匹配的单词。
		 it2->suffix().str().substr(0,6) << endl; //后缀的第一部分。
}

3、使用子表达式

  子表达式通常用于数据验证。

  正则表达式的一些特性:

  • \d{n}:表示一个n个数字的序列。
  • [-. ]:表示字符集合匹配这些字符中任意一个。
  • ?:表示组件是可选的。
  • \(:双斜线去掉C++中的特殊字符。
  • (\d{3}):每个字表达式用一对括号包围。
/*************
*使用子表达式。
**************/

string phone = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ])?(\\d{4})";
regex r3(phone);
smatch result3;
string s3;
while (getline(cin, s3))
{
	for (sregex_iterator it3(s3.begin(), s3.end(), r3), 
		 end_it; it3 != end_it; ++it3)
	{
		if(valid(*it3))
			cout << "valid: " << it3->str() << endl;
		else
			cout << "not valid: " << it3->str() << endl;
	}
}

4、使用regex_replace

bool valid(const smatch &m)
{
	//如果区号前有一个左括号。
	if(m[1].matched)//则区号后必须有右括号,之后紧跟剩余号码或者一个空格。
		return m[3].matched && (m[4].matched == 0 || m[4].str() == " ");
	else//否则区号后不能有右括号,另外两个分隔符必须匹配。
		return !m[3].matched && m[4].str() == m[6].str();
}

/******************
*使用regex_replace。
*******************/
string phone2 = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ])?(\\d{4})";
regex r4(phone2);
string s4 = "(908) 555-1800 862.555.0123";
string fmt = "$2.$5.$7 ";
cout << regex_replace(s4, r4, fmt, std::regex_constants::format_no_copy) << endl;

5、本节demo:

#include <iostream>
#include <regex>
using namespace std;

bool valid(const smatch &m)
{
	//如果区号前有一个左括号。
	if(m[1].matched)//则区号后必须有右括号,之后紧跟剩余号码或者一个空格。
		return m[3].matched && (m[4].matched == 0 || m[4].str() == " ");
	else//否则区号后不能有右括号,另外两个分隔符必须匹配。
		return !m[3].matched && m[4].str() == m[6].str();
}

int main(int argc, char const *argv[])
{

	/*****************
	*使用正则表达式库。
	******************/

	//[^]表示非。
	string pattern1_1("[^c]ei");
	//前后匹配任意大小写的零个或多个字母。
	pattern1_1 = "[[:alpha:]]*" + pattern1_1 + "[[:alpha:]]*";
	regex r1_1(pattern1_1);//创建用于查找模式的regex。
	smatch result1_1;
	string test_string1_1 = "receipt freid theif receive";
	//寻找第一个与正则表达式匹配的子序列,找到就停止。
	if(regex_search(test_string1_1, result1_1, r1_1))
		cout << result1_1.str() << endl;

	/*
	*$表示锚定行尾,只匹配$之前的内容。
	*.表示匹配其后的任意字符。
	*\\.表示去掉特殊含义,
	*regex::icase指定regex对象的选项,表示在匹配过程中忽略大小写。
	*正则表达式类型必须与输入类型匹配。smatch与string匹配,cmatch与char匹配。
	*/
	try
	{
		regex r1_2("([[:alpha:]]*)(\\.)([[:alpha:]]*)", regex::icase);
		smatch result1_2;
		string test_string1_2 = "abc.cpp";
		if(regex_search(test_string1_2, result1_2, r1_2))
			cout << result1_2.str(3) << endl;//使用子表达式,打印第三个子表达式。
	}
	catch (regex_error e)
	{
		//what()描述错误类型,code()表示错误编码。
		cout << e.what() << "\ncode: " << e.code() << endl;
	}

	/**********************
	*匹配与regex迭代器类型。
	***********************/

	string pattern2("[^c]ei");
	pattern2 = "[[:alpha:]]*" + pattern2 + "[[:alpha:]]*";
	regex r2(pattern2, regex::icase);//创建用于查找模式的regex。
	string test_string2 = "receipt freid abcdef theif receive";
	//sregex_iterator将迭代器定位到第一个匹配的位置。
	for (sregex_iterator it2(test_string2.begin(), test_string2.end(), r2), 
							end_it; it2 != end_it; ++it2)
	//打印出匹配单词的上下文。
	{
		auto pos = it2->prefix().length();//前缀的大小。
		pos = pos > 10 ? pos - 6 : 0;//最多需要6个字符。
		cout << it2->prefix().str().substr(pos) << //前缀的最后一部分。
			 "\n\t\t>>>" << it2->str() << " <<<\n" << //匹配的单词。
			 it2->suffix().str().substr(0,6) << endl; //后缀的第一部分。
	}

	/*************
	*使用子表达式。
	**************/

	string phone = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ])?(\\d{4})";
	regex r3(phone);
	smatch result3;
	string s3;
	while (getline(cin, s3))
	{
		for (sregex_iterator it3(s3.begin(), s3.end(), r3), 
			 end_it; it3 != end_it; ++it3)
		{
			if(valid(*it3))
				cout << "valid: " << it3->str() << endl;
			else
				cout << "not valid: " << it3->str() << endl;
		}
	}

	/******************
	*使用regex_replace。
	*******************/
	string phone2 = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ])?(\\d{4})";
	regex r4(phone2);
	string s4 = "(908) 555-1800 862.555.0123";
	string fmt = "$2.$5.$7 ";
	cout << regex_replace(s4, r4, fmt, std::regex_constants::format_no_copy) << endl;
	
	system("pause");
	return 0;
}

这里写图片描述

四、 随机数

1、随机数简介:

  随机数库的组成:
引擎类型:生成随机unsigned整数序列
分布类型:使用引擎返回服从特定概率分布的随机数。
  随机数引擎操作:
这里写图片描述
  分布类型的操作:
这里写图片描述

#include <iostream>
#include <random>
using namespace std;
vector<unsigned> bad_randvec()
{
	//将引擎和分布对象都定义为static,从而避免每次都生成相同的序列。
	static default_random_engine e;
	static uniform_int_distribution<unsigned> u(0, 9);
	vector<unsigned> ret;
	for (size_t i = 0; i < 10; ++i)
	{
		ret.push_back(u(e));
	}
	return ret;
}

int main(int argc, char const *argv[])
{
	//引擎生成一个数值序列。
	vector<unsigned> v1(bad_randvec());
	vector<unsigned> v2(bad_randvec());
	cout << ((v1 == v2) ? "equal" : "not equal" ) << endl;


	//设置随机数发生器种子。
	default_random_engine e2;
	vector<string> vec2{"Hei Tao", "Hong Tao", "Mei Hua","Fang Kuai"};
	uniform_int_distribution<int> u2(0, 3);
	for (size_t i = 0; i < 5; ++i)
	{
		cout << vec2[u2(e2)] << endl;
	}

	//其他随机数分布。
	default_random_engine e3;
	normal_distribution<double> n(0, 1);//定义正态分布对象。
	for (size_t i = 0; i < 10; ++i)
	{
		cout << n(e3) << " ";
	}
	cout << endl;
	
	string resp;
	default_random_engine e4;//引擎应该在循环外定义。
	bernoulli_distribution b4(.55);//程序有55%的概率先行。
	do
	{
		bool first = b4(e4);
		cout << (first ? "we go first" : "you go first") << endl;
	} while (cin >> resp && resp[0] == 'y');

	system("pause");
	return 0;
}

这里写图片描述

2、random_shuffle()方法:

  产生指定范围内的随机元素集的最佳方法是创建一个顺序序列(也就是向量或者内置数组),在这个顺序序列中含有指定范围的所有值。填充完向量之后,用random_shuffle()算法打乱元素排列序。
  random_shuffle()定义在标准的头文件中。因为所有的STL算法都是在名字空间std::中声明的,所以你要注意正确地声明数据类型。
random_shuffle()有两个参数,第一个参数是指向序列首元素的迭代器,第二个参数则指向序列最后一个元素的下一个位置。下列代码段用random_shuffle()算法打乱了先前填充到向量中的元素:

include <algorithm>  
using std::random_shuffle;  
random_shuffle(vi.begin(), vi.end()); /* 打乱元素 */  

  例子:

#include "stdafx.h"  
#include <iostream>  
#include <vector>  
#include <algorithm>  
using namespace std;  
  
int _tmain(int argc, _TCHAR* argv[])  
{  
     vector<string> str;  
     str.push_back("hello");  
     str.push_back("world");  
     str.push_back("welcome");  
     str.push_back("to");  
     str.push_back("Beijing");  
  
     std::random_shuffle(str.begin(),str.end());//迭代器  
  
     for(int j = 0; j < str.size(); j++)  
     {  
         cout<<str[j].c_str()<<" ";  
     }  
     cout<<endl;  
   
    system("pause");  
    return 0;  
}  

五、IO库再探

  操作符用于两大类输出控制:控制数值的输出形式以及控制补白的数量和位置。大多数改变格式状态的操纵符都是设置/复原成对的;一个操纵符用来将格式状态设置为一个新值,而另一个用来将其复原,恢复为正常的默认格式。

#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main(int argc, char const *argv[])
{

	/***************
	*格式化输入输出。
	****************/

	//用boolalpha控制bool值的输出。
	cout << true << " " << false << boolalpha << "\n" << true << " " << false << endl;

	//控制整型值的输出:hex(16)、oct(8)、dec(2)
	cout << showbase;//显示进制符。
	cout << 20 << endl;
	cout << hex << 20 << endl;
	cout << oct << 20 << endl;
	cout << dec << 20 << endl;

	//控制精度、浮点数计数法、打印小数、输出补白(位宽)
	cout << setprecision(3);//设置精度。

	cout << "precision: " << cout.precision() << ", value: " << sqrt(2) << endl;
	cout << scientific << 10 * sqrt(2) << endl;//科学计数法。
	cout << defaultfloat << 10 * sqrt(2) << endl;//默认操纵符。

	cout << setprecision(3);
	cout << showpoint << sqrt(2) << endl;//强制显示小数点。

	int i = 16;
	double d = 3.1415926;

	cout << "i: " << setw(12) << i << "next col" << "\n"
		 << "d: " << setw(12) << d << "next col" << "\n";

    cout << left;
	cout << "i: " << setw(12) << i << "next col" << "\n"
		 << "d: " << setw(12) << d << "next col" << "\n";

    cout << right;
	cout << "i: " << setw(12) << i << "next col" << "\n"
		 << "d: " << setw(12) << d << "next col" << "\n";

    cout << setfill('#');
	cout << "i: " << setw(12) << i << "next col" << "\n"
		 << "d: " << setw(12) << d << "next col" << "\n" << setfill(' ');//恢复正常的补白字符。
	return 0;
}

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

~青萍之末~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值