欢迎访问我的博客首页。
标准库特殊设施
1. tuple 类型
Python 中的元组 tuple 使用小括号表示,类似 Python 的列表 list,但创建后不能修改。C++ 中的 tuple 也可以存放任意数量类型不同的成员。而且它是一个模板类型,因此初始化后的 tuple 也是不能添加、修改的。
tuple 本身支持初始化、比较、访问操作。如果我们希望将一些数据组合成单一对象,而又不需要对它们进行复杂操作,就可以使用 tuple 而不是类或结构体,比如可以借助 tuple 返回多个值。
下面是 tuple 的用法。在 C++ 新标准中,tuple 支持列表初始化。
// 1.1 先定义再初始化。
tuple<int, char> t1;
t1 = {1, 'c'};
// 1.2 定义时初始化。
tuple<int, char> t2(1, 'c');
// 1.3 使用 make_tuple。
tuple<int, char> t3 = make_tuple(1, 'c');
// 1.4 使用初始化列表。
tuple<int, char> t4 = {1, 'c'};
// 2. 访问。
cout << get<0>(t4) << " " << get<1>(t4) << endl;
// 3.1 已知 tuple 类型时获取元素个数(从模板参数就可以看出该类型的 tuple 有两个元素)。
size_t sz1 = tuple_size<tuple<int, char>>::value;
cout << sz1 << endl;
// 3.2 未知 tuple 类型时获取元素个数。
typedef decltype(t4) trans; // 先获取类型 trans。
size_t sz2 = tuple_size<trans>::value;
cout << sz2 << endl;
// 4. 获取下标为 1 类型未知的元素。
tuple_element<1, trans>::type cnt = get<1>(t3);
cout << cnt << endl;
// 5. 比较。
tuple<int, char> t5{1, 'b'};
cout << (t5 < t4) << endl; // true
2. bitset 类型
3. 正则表达式
3.1 匹配函数
如果整个输入序列与表达式匹配,则regex_match函数返回true;如果输入序列中一个子串与表达式匹配,则regex_search函数返回true;如果要在输入序列中查找并替换一个正则表达式,则使用regex_replace。
1. 函数regex_match
void regexMatch(const char* input, const char* expr) {
string str(input);
smatch results;
regex reg(expr);
if (regex_match(str, results, reg)) {
for(string x: results)
cout << x << endl;
}
}
int main() {
regexMatch("a cd", "[a-z][ ]{2}[a-z]*");
regexMatch("ab123c.cpp", "([^0-9][[:alnum:]]*)\\.(cpp)");
}
如果匹配成功,第三行的result[0]是整个输入序列。如果像第12行那样使用小括号把表达式分组,result[0]后面的元素就是各分组匹配的输入序列子串。
2. 函数regex_search
3. 函数regex_replace
void regexReplace(const char* input, const char* expr, const char* fmt) {
string str1(input);
regex reg(expr);
string str2(fmt);
cout << regex_replace(str1, reg, str2) << endl;
}
int main() {
regexReplace("192-168-1-0", "([[:digit:]]+)-([[:digit:]]+)-([[:digit:]]+)-([[:digit:]]+)", "$1.$2.$3.$4");
regexReplace("ab123c.cpp", "([[:alnum:]]*)\\.(cpp)", "$1.cxx");
}
regex_replaceh函数的第三个参数指定格式,实参在第8行:第2个参数使用小括号把表达式分组,第3个参数指定各分组的组合方式。
3.2 字符匹配
字母 | 数字 | 字母或数字 | 字符 |
---|---|---|---|
[[:alpha:]]等价于([a-z] | [A-Z]) | [[:digit:]]等价于[0-9] | [[:alnum:]]等价于([a-z] | [A-Z] | [0-9]) | . |
4. 随机数
4.1 函数rand()
函数rand()产生[0, RAND_MAX]范围内的随机数。如果不使用srand()函数设置种子,每次运行时产生的多个随机数一般不相等,但程序多次运行的结果是相同的。
cout << RAND_MAX << endl; // 32767 = 2^15-1。
srand((unsigned)time(NULL));
cout << rand() << endl;
产生一组 [a, b] 之间的随机数,每次运行不相同。注意第 6 行不能写到函数 fun 内!
int fun(int a, int b) {
return rand() % (b - a + 1) + a;
}
int main() {
srand((unsigned)time(NULL));
for (int i = 0; i < 10; i++)
cout << fun(2, 9) << " ";
system("pause");
}
4.2 随机数引擎和分布
1. 随机数引擎
default_random_engine e((unsigned)time(NULL));
cout << e.min() << " " << e.max() << endl; // [0, 4294967295=2^32-1]。
cout << e() << endl;
2. 分布类型和引擎
使用一个分布类型的对象产生指定范围的随机数。
// uniform_int_distribution<int> u(-9, 9); // 产生[-9, 9]之间的随机数。
uniform_int_distribution<unsigned> u(0, 9);
default_random_engine e((unsigned)time(NULL));
cout << u(e) << endl;
3. 引擎生成一个数值序列
和rand()函数一样,如果不设置种子,使用随机数引擎生成的随机数多次运行结果是相同的。上面的程序设置随机数引擎的种子为(unsigned)time(NULL)。
4.3 随机数例子
下面我们就使用 C++ 按高斯分布产生随机数。
#include <iostream>
#include <random>
int main(void) {
#if true
// 每次生成的随机数不同。
std::random_device rd;
std::mt19937 gen(rd());
#else
// 每次生成的随机数相同。
default_random_engine gen;
#endif
// 设定均值为 0,标准差为 1。
std::normal_distribution<double> normal(0.0, 1.0);
for (int i = 0; i < 10; i++) {
std::cout << normal(gen) << " ";
}
std::cout << std::endl;
return 0;
}
4.3 其他随机数分布
5. IO 库再探
6. 时间戳
时间戳可以用于设置随机数种子、文件命名、计时等。
纪元时间是1970年1月1日0点整。但中国在东8区,纪元时间是1970年1月1日8点整,比如上午9点获取的自纪元至今的小时数是x,则
x
=
24
n
+
1
x = 24n + 1
x=24n+1。
1. C库函数time
time_t t1;
time_t t2 = time(&t1);
返回自纪元至今经过的秒数。如果参数不为空(0或NULL),返回值也存储在参数中。类型time_t是有符号的long long类型。
2. C++库chrono
#include<chrono>
using namespace std;
using namespace std::chrono;
int main() {
// 纳秒(精确到100纳秒)
time_point<system_clock, nanoseconds> clock = time_point_cast<nanoseconds>(system_clock::now());
time_t time = clock.time_since_epoch().count();
cout << time << endl;
}
上面输出自纪元至今经过的百纳秒数。使用microseconds、milliseconds、seconds、minutes、hours输出微秒、毫秒、秒、分钟、小时。其中使用seconds的输出和使用C库函数time(NULL)的输出是一样的。
3. 获取当前时间
#include <chrono>
#include <ctime>
#include <iostream>
#include <sstream>
using namespace std;
void f1() {
time_t rawtime;
struct tm info;
char buffer[80];
time(&rawtime);
localtime_s(&info, &rawtime);
strftime(buffer, 80, "%Y%m%d_%H%M%S", &info);
cout << "f1: " << buffer << endl;
}
void f2() {
std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
std::time_t time_now = std::chrono::system_clock::to_time_t(tp);
char buf[250];
// 时间。
ctime_s(buf, sizeof(buf), &time_now);
cout << "f2_1: " << buf;
// 时间戳。
memset(buf, 0, sizeof(buf));
tm local_time;
localtime_s(&local_time, &time_now);
strftime(buf, sizeof(buf), "%Y%m%d_%H%M%S", &local_time);
cout << "f2_2: " << buf << endl;
}
void f3() {
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>
clock = std::chrono::time_point_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now());
time_t time = clock.time_since_epoch().count();
cout << "f3: " << time << endl;
}
std::string GetCurrentTimeStamp(int time_stamp_type = 0) {
std::chrono::system_clock::time_point now =
std::chrono::system_clock::now();
std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
// std::tm *now_tm = std::localtime(&now_time_t);
tm local_time;
localtime_s(&local_time, &now_time_t);
char buffer[128];
strftime(buffer, sizeof(buffer), "%F %T", &local_time);
std::ostringstream ss;
ss.fill('0');
std::chrono::milliseconds ms;
std::chrono::microseconds cs;
std::chrono::nanoseconds ns;
switch (time_stamp_type) {
case 0:
ss << buffer;
break;
case 1:
ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) %
1000;
ss << buffer << ":" << ms.count();
break;
case 2:
ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) %
1000;
cs = std::chrono::duration_cast<std::chrono::microseconds>(
now.time_since_epoch()) %
1000000;
ss << buffer << ":" << ms.count() << ":" << cs.count() % 1000;
break;
case 3:
ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) %
1000;
cs = std::chrono::duration_cast<std::chrono::microseconds>(
now.time_since_epoch()) %
1000000;
ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
now.time_since_epoch()) %
1000000000;
ss << buffer << ":" << ms.count() << ":" << cs.count() % 1000 << ":"
<< ns.count() % 1000;
break;
default:
ss << buffer;
break;
}
return ss.str();
}
void f4() {
std::cout << GetCurrentTimeStamp(0) << std::endl;
std::cout << GetCurrentTimeStamp(1) << std::endl;
std::cout << GetCurrentTimeStamp(2) << std::endl;
std::cout << GetCurrentTimeStamp(3) << std::endl;
}
int main() {
f1();
f2();
f3();
f4();
return 0;
}
7. 参考文献
- C++高斯分布随机数生成,优快云,2020。
- C++ 时间,脚本之家,2023。