C++ 开发常用代码块之一

日期处理

获取时间戳

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <chrono>

using namespace std;

// 获取时间戳(秒数)
// dateTime: 日期时间字符串,格式:2021-01-08 21:27:00
long getTimestamp(const string &dateTime) {
tm tm = {};
strptime(dateTime.c_str(), "%Y-%m-%d %H:%M:%S", &tm);
chrono::system_clock::time_point tp = chrono::system_clock::from_time_t(mktime(&tm));
long milliseconds = chrono::duration_cast<chrono::milliseconds>(tp.time_since_epoch()).count();
return milliseconds / 1000;
}

int main() {
long timestamp = getTimestamp("2021-01-08 21:27:00");
cout << "timestamp = " << timestamp << endl;
return 0;
}

程序运行输出的结果如下:

1
timestamp = 1610112420

格式化当前时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <time.h>

using namespace std;

// 格式化当前时间
// 默认格式是: 2020-06-07 23:46:53
string formatCurrentTime() {
time_t rawtime;
struct tm* info;
char buffer[80];

time(&rawtime);
info = localtime(&rawtime);
strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);
string str(buffer);
return str;
}

// 格式化当前时间
// format: 格式字符串,例如 %Y-%m-%d %H:%M:%S
string formatCurrentTime(string format) {
time_t rawtime;
struct tm* info;
char buffer[80];

time(&rawtime);
info = localtime(&rawtime);
strftime(buffer, 80, format.c_str(), info);
string str(buffer);
return str;
}

int main() {
cout << formatCurrentTime() << endl;
cout << formatCurrentTime("%Y-%m-%d") << endl;
}

程序运行输出的结果如下:

1
2
2021-11-22 22:52:43
2021-11-22

计算指定的日期是星期几

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <sstream>

using namespace std;

// 根据给定的日期,计算它是星期几
// date: 日期字符串,格式是: 2021-12-01
// 返回值:1, 2, 3, 4, 5, 6, 0, 其中 0 表示星期日
int dayOfWeek(const string &date) {
char c;
int y, m, d;
stringstream(date) >> y >> c >> m >> c >> d;
tm t = {0, 0, 0, d, m - 1, y - 1900};
mktime(&t);
return t.tm_wday;
}

// 根据给定的日期,判断是否为周末
// date: 日期字符串,格式是: 2021-12-01
bool isWeekendDays(const string &date) {
int wday = dayOfWeek(date);
if (wday == 6 || wday == 0) {
return true;
}
return false;
}

int main() {
cout << dayOfWeek("2022-01-07") << endl;
cout << dayOfWeek("2022-01-08") << endl;
cout << dayOfWeek("2022-01-09") << endl;
cout << dayOfWeek("2022-01-10") << endl;
cout << (isWeekendDays("2022-01-09") ? "true" : "false") << endl;
}

程序运行输出的结果如下:

1
2
3
4
5
5
6
0
1
true

计算两个日期之间的天数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>

using namespace std;

// 判断一个年份是否为闰年
bool isLeap(int year) {
return (year % 4 == 0 || year % 400 == 0) && (year % 100 != 0);
}

// 计算特定年份的天数
int daysOfYear(int year) {
return isLeap(year) ? 366 : 365;
}

// 根据给定的日期,计算它在该年的第几天
int dayOfYear(int year, int month, int day) {
int DAY[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (isLeap(year)) {
DAY[1] = 29;
}
for (int i = 0; i < month - 1; ++i) {
day += DAY[i];
}
return day;
}

// 判断日期字符串是否合法,并分别取出日期中的年月日
// date: 日期字符串,格式是: 20211201
bool stringToDate(string date, int& year, int& month, int& day) {
year = atoi(date.substr(0, 4).c_str());
month = atoi(date.substr(4, 2).c_str());
day = atoi(date.substr(6, 2).c_str());
int DAY[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (isLeap(year)) {
DAY[1] = 29;
}
return year >= 0 && month <= 12 && month > 0 && day <= DAY[month - 1] && day > 0;
}

// 计算两个日期之间的天数
// date1: 日期字符串,格式是: 20211201
// date2: 日期字符串,格式是: 20211201
// 当返回值为 -1 时,说明日期的格式不正确
int daysBetween2Date(string date1, string date2) {
int year1, month1, day1;
int year2, month2, day2;
if (!stringToDate(date1, year1, month1, day1) || !stringToDate(date2, year2, month2, day2)) {
cout << "输入的日期格式不正确";
return -1;
}
if (year1 == year2 && month1 == month2) {
return day1 > day2 ? day1 - day2 : day2 - day1;
}
else if (year1 == year2) {
int d1, d2;
d1 = dayOfYear(year1, month1, day1);
d2 = dayOfYear(year2, month2, day2);
return d1 > d2 ? d1 - d2 : d2 - d1;
}
else {
// 确保year1年份比year2早
if (year1 > year2) {
swap(year1, year2);
swap(month1, month2);
swap(day1, day2);
}
// 计算第一个日期在该年还剩多少天
int d1, d2, d3;
if (isLeap(year1)) {
d1 = 366 - dayOfYear(year1, month1, day1);
}
else {
d1 = 365 - dayOfYear(year1, month1, day1);
}
// 计算第二日期在当年中的第几天
d2 = dayOfYear(year2, month2, day2);
// 计算两个年份相隔的天数
d3 = 0;
for (int year = year1 + 1; year < year2; year++) {
if (isLeap(year))
d3 += 366;
else
d3 += 365;
}
return d1 + d2 + d3;
}
}

int main() {

int days = daysBetween2Date("20101111", "20111111");
cout << "相差 " << days << " 天" << endl;

int days2 = daysBetween2Date("20200202", "20200131");
cout << "相差 " << days2 << " 天" << endl;

int days3 = daysBetween2Date("20230712", "20050619");
cout << "相差 " << days3 << " 天" << endl;

return 0;
}

程序运行输出的结果如下:

1
2
3
相差 365 天
相差 2 天
相差 6597 天

加载动态库

加载动态库(.so)

提示

下述示例代码,适用于 Linux 系统的 C++ 开发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

int main()
{
int a = 0;

// 加载动态库
void *handle = dlopen("./libadd_c.so", RTLD_LAZY);

if(!handle)
{
printf("open lib error\n");
cout<<dlerror()<<endl;
return -1;
}

// 定义函数指针类型
typedef int (*add_t)(int a, int b);

// 调用动态库
add_t add = (add_t) dlsym(handle, "add");
if(!add)
{
cout<<dlerror()<<endl;
dlclose(handle);
return -1;
}
a = add(3, 4);
printf("a = %d\n",a);

// 释放动态库
dlclose(handle);
return 0;
}

加载动态链接库(.dll)

提示

下述示例代码,适用于 Windows 系统的 C++ 开发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <windows.h>

using namespace std;

int main() {

HINSTANCE hInstance;

// 加载动态链接库
hInstance = LoadLibrary("./socketclient.dll");
if (hInstance == NULL)
{
printf("LoadLibrary() 调用失败, ErrorCode: %d", GetLastError());
return -1;
}

// 定义函数类型指针
typedef int (*CltSocketInit)(void** handle);

// 调用动态链接库
CltSocketInit cltSocketInit = (CltSocketInit)GetProcAddress(hInstance, "cltSocketInit");
if (cltSocketInit != NULL)
{
void* handle = NULL;
int result = cltSocketInit(&handle);
printf("result = %d", result);
}

// 释放动态链接库
if (hInstance != NULL) {
FreeLibrary(hInstance);
}

return 0;
}

任务调度

定时器

提示

基于 C++ 11 实现等价于 Javascript 的 setTimeout()setInterval() 函数。

  • timer.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>

using namespace std;

class Timer {

public:
typedef void(TimerFunction)();

public:
void setTimeout(TimerFunction, long delay);
void setInterval(TimerFunction, long interval);
void stop();

private:
atomic<bool> active{ true };

};

void Timer::setTimeout(TimerFunction function, long delay) {
active = true;
thread t([=]() {
if (!active.load()) return;
this_thread::sleep_for(chrono::milliseconds(delay));
if (!active.load()) return;
function();
});
t.detach();
}

void Timer::setInterval(TimerFunction function, long interval) {
active = true;
thread t([=]() {
while (active.load()) {
this_thread::sleep_for(chrono::milliseconds(interval));
if (!active.load()) return;
function();
}
});
t.detach();
}

void Timer::stop() {
active = false;
}
  • main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <conio.h>
#include <iostream>
#include "timer.h"

using namespace std;

void refreshConfig() {
cout << "execute refresh config ..." << endl;
}

int main() {
// 使用智能指针
unique_ptr<Timer> timer(new Timer());
Timer::TimerFunction* refreshFunc = refreshConfig;
timer->setInterval(refreshConfig, 3000);
timer->setTimeout(refreshConfig, 5000);
_getch();
return 0;
}

程序运行输出的结果如下:

1
2
3
4
5
execute refresh config ...
execute refresh config ...
execute refresh config ...
execute refresh config ...
execute refresh config ...

线程休眠

sleep() 函数的功能是让程序的执行挂起一段时间,也就是等待一段时间再继续往下执行。

不同平台和不同编译器的区别

  • sleep() 函数在 Linux 平台的头文件是 unistd.h
  • sleep() 函数在 Windows 平台的头文件是 windows.h
  • sleep() 函数的名称是区分大小写的,有的编译器是大写,有的是小写
  • sleep() 函数休眠的时间,在 Windows 平台下是以 毫秒 为单位,而在 Linux 平台是以 为单位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Windows平台的写法
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
int main()
{
int a = 1;
while (a)
{
printf("Welcome to songjiahao's blog\n");
Sleep(1000);
}
system("pause");
return 0;
}

文件处理

读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <fstream>

using namespace std;

int main() {
ifstream ifs;
ifs.open("/tmp/file.txt", ios::in);
if (!ifs.is_open()) {
cout << "文件打开失败" << endl;
return -1;
}

// 按行读取
string line;
while (getline(ifs, line)) {
cout << line << endl;
}

ifs.close();

return 0;
}

写入文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <fstream>

using namespace std;

// 写文件
void writeFile() {
ofstream ofs;
ofs.open("/tmp/file.txt");
if (!ofs.is_open()) {
cout << "文件打开失败" << endl;
return;
}

ofs << "Hello World 1" << endl;
ofs << "Hello World 2" << endl;
ofs << "Hello World 3" << endl;
ofs.flush();
ofs.close();
}

// 以追加的方式写入文件
void writeFileAppend() {
ofstream ofs;
ofs.open("/tmp/file.txt", ios::app);
if (!ofs.is_open()) {
cout << "文件打开失败" << endl;
return;
}

ofs << "Append content" << endl;
ofs.flush();
ofs.close();
}

创建文件夹

提示

下述 C++ 代码兼容 Linux 与 Windows 平台,用于创建文件夹以及子文件夹

  • fileutil.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#pragma once

#ifdef WIN32
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#include <sys/stat.h>
#endif

#ifdef WIN32
#define ACCESS(fileName, accessMode) _access(fileName, accessMode)
#define MKDIR(path) _mkdir(path)
#else
#define ACCESS(fileName, accessMode) access(fileName, accessMode)
#define MKDIR(path) mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
#endif

#include <iostream>
#define MAX_PATH_LEN 256

using namespace std;

int32_t createDirectory(const string &dirPath);
  • fileutil.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "fileutil.h"

// 根据目录路径,从左到右依次判断目录是否存在,不存在则创建
// 注意:最后一个如果是目录的话,则必须加上 '\\' 或者 '/'
// 示例: /usr/local/scripts/
int32_t createDirectory(const string &dirPath) {
uint32_t dirPathLen = dirPath.length();
if (dirPathLen > MAX_PATH_LEN) {
return -1;
}
char tmpDirPath[MAX_PATH_LEN] = {0};
for (uint32_t i = 0; i < dirPathLen; ++i) {
tmpDirPath[i] = dirPath[i];
if (tmpDirPath[i] == '\\' || tmpDirPath[i] == '/') {
if (ACCESS(tmpDirPath, 0) != 0) {
int32_t ret = MKDIR(tmpDirPath);
if (ret != 0) {
return ret;
}
}
}
}
return 0;
}

字符串处理

分割字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <vector>

using namespace std;

// 分割字符串
// str: 要分割的字符串
// delim: 分割字符
vector<string> split(const string &str, const char &delim = ' ') {
vector<string> tokens;
size_t lastPos = str.find_first_not_of(delim, 0);
size_t pos = str.find(delim, lastPos);
while (lastPos != string::npos) {
tokens.emplace_back(str.substr(lastPos, pos - lastPos));
lastPos = str.find_first_not_of(delim, pos);
pos = str.find(delim, lastPos);
}
return tokens;
}

int main() {
vector<string> strResult = split("Hello,World,!", ',');
for (auto it = strResult.begin(); it != strResult.end(); it++) {
cout << *it << " ";
}
}

程序运行输出的结果如下:

1
Hello World !

去除字符串两边的空格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

using namespace std;

// 去除字符串两边的空格
void trim(string &str) {
if (str.empty()) {
return;
}
str.erase(0, str.find_first_not_of(" "));
str.erase(str.find_last_not_of(" ") + 1);
}

int main() {
string str = " hello ";
trim(str);
cout << "str=" << str << endl;
string str2 = str + "world";
cout << "str2=" << str2 << endl;
}

程序运行输出的结果如下:

1
2
str=hello
str2=helloworld

判断字符串是否为空串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>

using namespace std;

// 去除字符串两边的空格
void trim(string &str) {
if (str.empty()) {
return;
}
str.erase(0, str.find_first_not_of(" "));
str.erase(str.find_last_not_of(" ") + 1);
}

// 判断字符串是否为空串
// "" -> true
// " " -> true
// "a" -> false
// " a " -> false
bool empty(const string &str) {
if (str.empty()) {
return true;
}
string strTemp = str;
trim(strTemp);
return strTemp.length() == 0;
}

int main() {
string str1 = "";
string str2 = " ";
string str3 = "a";
string str4 = " a ";
cout << (empty(str1) ? "true" : "false") << endl;
cout << (empty(str2) ? "true" : "false") << endl;
cout << (empty(str3) ? "true" : "false") << endl;
cout << (empty(str4) ? "true" : "false") << endl;
}

程序运行输出的结果如下:

1
2
3
4
true
true
false
false

转换字符集编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 // Gb2312 转换 Utf8 编码
// iInLen的长度不包括'\0'字符,应该用strlen(),返回值是处理后的sOut长度
int Gb2312ToUtf8(char *sOut, int iMaxOutLen, const char *sIn, int iInLen) {
char *pIn = (char *) sIn;
char *pOut = sOut;
size_t ret;
size_t iLeftLen = iMaxOutLen;
iconv_t cd;

cd = iconv_open("utf-8", "gb2312");
if (cd == (iconv_t) -1) {
return -1;
}
size_t iSrcLen = iInLen;
ret = iconv(cd, &pIn, &iSrcLen, &pOut, &iLeftLen);
if (ret == (size_t) -1) {
iconv_close(cd);
return -1;
}

iconv_close(cd);
return (iMaxOutLen - iLeftLen);
}

// Utf8 转换 Gb2312 编码
// iInLen的长度不包括'\0'字符,应该用strlen(),返回值是处理后的sOut长度
int Utf8ToGb2312(char *sOut, int iMaxOutLen, const char *sIn, int iInLen) {
char *pIn = (char *) sIn;
char *pOut = sOut;
size_t ret;
size_t iLeftLen = iMaxOutLen;
iconv_t cd;

cd = iconv_open("gb2312", "utf-8");
if (cd == (iconv_t) -1) {
return -1;
}
size_t iSrcLen = iInLen;
ret = iconv(cd, &pIn, &iSrcLen, &pOut, &iLeftLen);
if (ret == (size_t) -1) {
iconv_close(cd);
return -1;
}

iconv_close(cd);
return (iMaxOutLen - iLeftLen);
}

标准 I/O 流

让用户输入指定范围内的数字

让用户输入一个 1 到 10 的数字,如果输入有误,提示重新输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <limits>

using namespace std;

int main() {
int num;
cout << "请输入一个 1 到 10 之间的数字:" << endl;

while (true) {
cin >> num;
// 判断输入流的错误标志位, 0 - 正常的, 1 - 不正常的
if (cin.fail()) {
// 重置输入流的错误标志位
cin.clear();
// 在输入流中忽略(跳过)剩余的输入内容, 直到遇到换行符为止, 确保后续的输入操作是干净的, 不会受到之前无效输入的影响
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "输入无效,请输入一个数字" << endl;
} else if (num >= 1 && num <= 10) {
cout << "输入的数字为:" << num << endl;
break;
} else {
cout << "数字不在范围内,请重新输入" << endl;
}
}

return 0;
}