Linux时间操作
UNIX操作系统根据计算机产生的年代把1970年1月1日作为UNIX的纪元时间,1970年1月1日是时间的中间点,将从1970年1月1日起经过的秒数用一个整数存放。
time_t
time_t用于表示时间类型,它是long类型。表示从1970年1月1日0时0分0秒到现在的秒数。
time()
time()用于获取操作系统当前时间。
#include <time.h>
#include <iostream>
using namespace std;
int main()
{
time_t now=time(0);
cout << now << endl;
time_t now2;
time(&now2);
cout << now2 << endl;
return 0;
}
tm结构体
struct tm
{
int tm_year; // 年份:其值等于实际年份减去1900
int tm_mon; // 月份:取值区间为[0,11],其中0代表一月,11代表12月
int tm_mday; // 日期:一个月中的日期,取值区间为[1,31]
int tm_hour; // 时:取值区间为[0,23]
int tm_min; // 分:取值区间为[0,59]
int tm_sec; // 秒:取值区间为[0,59]
int tm_wday; // 星期:取值区间为[0,6],其中0代表星期天,6代表星期六
int tm_yday; // 从每年的1月1日开始算起的天数:取值区间为[0,365]
int tm_isdst; // 夏令时标识符,该字段意义不大
};
localtime()
localtime()函数用于把time_t表示的时间转换为tm结构体表示的时间。
localtime()函数不是线程安全的,localtime_r()是线程安全的。
函数声明
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);
示例
#include <iostream>
#include <time.h> // 时间操作的头文件。
using namespace std;
int main()
{
time_t now=time(0); // 获取当前时间,存放在now中。
cout << "now=" << now << endl; // 显示当前时间,1970年1月1日到现在的秒数。
tm tmnow;
localtime_r(&now,&tmnow); // 把整数的时间转换成tm结构体。
// 根据tm结构体拼接成中国人习惯的字符串格式。
string stime = to_string(tmnow.tm_year+1900)+"-"
+ to_string(tmnow.tm_mon+1)+"-"
+ to_string(tmnow.tm_mday)+" "
+ to_string(tmnow.tm_hour)+":"
+ to_string(tmnow.tm_min)+":"
+ to_string(tmnow.tm_sec);
cout << "stime=" << stime << endl;
}
mktime()
mktime()函数的功能与localtime()函数相反,用于把tm结构体时间转换为time_t时间。
函数声明:
time_t mktime(struct tm *tm)
比如说把2022-02-28 24:00:00加30分钟,手写还要考虑闰年、进位等等问题,可以用mktime()当中间跳板先变秒数,加了再用localtime转成结构体。
#include <iostream>
#include <time.h>
using namespace std;
//把2022-02-28 24:00:00加30分钟
int main()
{
struct tm mytime;
mytime.tm_year = 2022 - 1900;
mytime.tm_mon = 2 - 1;
mytime.tm_mday = 28;
mytime.tm_hour = 24;
mytime.tm_min = 0;
mytime.tm_sec = 0;
time_t tmp = mktime(&mytime);
tmp += 30 * 60;
localtime_r(&tmp, &mytime);
string stime = to_string(mytime.tm_year+1900)+"-"
+ to_string(mytime.tm_mon+1)+"-"
+ to_string(mytime.tm_mday)+" "
+ to_string(mytime.tm_hour)+":"
+ to_string(mytime.tm_min)+":"
+ to_string(mytime.tm_sec);
cout << "加了30分钟后: " << stime << endl;
return 0;
}
gettimeofday()
用于获取1970年1月1日到现在的秒和当前秒中已逝去的微秒数
#include <iostream>
#include <sys/time.h> // gettimeofday()需要的头文件。
using namespace std;
int main()
{
timeval start, end;
gettimeofday(&start, 0); // 计时开始。
for (int ii = 0; ii < 1000000000; ii++)
;
gettimeofday(&end, 0); // 计时结束。
// 计算消耗的时长。
timeval tv;
tv.tv_usec = end.tv_usec - start.tv_usec;
tv.tv_sec = end.tv_sec - start.tv_sec;
if (tv.tv_usec < 0)
{
tv.tv_usec = 1000000 - tv.tv_usec;
tv.tv_sec--;
}
cout << "耗时:" << tv.tv_sec << "秒和" << tv.tv_usec << "微秒。\n";
}
程序睡眠
包含头文件:<unistd.h>
sleep是秒,usleep是微秒(百万分之一秒)
unsigned int sleep(unsigned int seconds);
int usleep(useconds_t usec);
Linux的目录操作
简单的目录操作函数
1) 获取当前工作目录
包含头文件:<unistd.h>
char *getcwd(char *buf, size_t size);
char *get_current_dir_name(void);
2)切换工作目录
包含头文件:<unistd.h>
int chdir(const char *path);
返回值:0-成功;其它-失败(目录不存在或没有权限)。
3)创建目录
包含头文件:<sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
pathname-目录名。
mode-访问权限,如0755,不要省略前置的0。
返回值:0-成功;其它-失败(目录不存在或没有权限)。
4)删除目录
包含头文件: <unistd.h>
int rmdir(const char *path);
返回值:0-成功;其它-失败(目录不存在或没有权限)。
示例:
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/stat.h>
using namespace std;
int main()
{
char path1[256]; // linux系统目录的最大长度是255。
getcwd(path1, 256);
cout << "path1=" << path1 << endl;
char* path2 = get_current_dir_name();
cout << "path2=" << path2 << endl;
free(path2); // 注意释放内存。malloc() new delete
// 在当前目录下创建test1目录,切换进test1目录后创建test2目录
char path3[256];
strcpy(path3, path1);
strcat(path3, "/test1");
mkdir(path3, 0755);
chdir(path3);
char path4[256];
strcpy(path4, path3);
strcat(path4, "/test2");
mkdir(path4, 0775);
// 然后删除test2目录
rmdir(path4);
}
获取目录中文件的列表
每次调用readdir(),函数返回struct dirent的地址,存放了本次读取到的内容。
struct dirent
{
long d_ino; // inode number 索引节点号。
off_t d_off; // offset to this dirent 在目录文件中的偏移。
unsigned short d_reclen; // length of this d_name 文件名长度。
unsigned char d_type; // the type of d_name 文件类型。
char d_name [NAME_MAX+1]; // file name文件名,最长255字符。
};
重点关注结构体的d_name和d_type成员。
d_name-文件名或目录名。
d_type-文件的类型,有多种取值,最重要的是8和4,8-常规文件(A regular file);4-子目录(A directory),其它的暂时不关心。注意,d_name的数据类型是字符,不可直接显示。
#include <iostream>
#include <dirent.h>
using namespace std;
int main(int argc, char* argv[])
{
if (argc != 2) { cout << "Using ./demo 目录名\n"; return -1; }
DIR* dir; // 定义目录指针。
// 打开目录。
if ((dir = opendir(argv[1])) == nullptr) return -1;
// 用于存放从目录中读取到的内容。
struct dirent* stdinfo = nullptr;
while (1)
{
// 读取一项内容并显示出来。
if ((stdinfo = readdir(dir)) == nullptr) break;
cout << "文件名=" << stdinfo->d_name << ",文件类型=" << (int)stdinfo->d_type << endl;
}
closedir(dir); // 关闭目录指针。
}
Linux的系统错误
strerror()
strerror() 在<string.h>中声明,用于获取错误代码对应的详细信息。
char *strerror(int errnum); // 非线程安全。
int strerror_r(int errnum, char *buf, size_t buflen); // 线程安全。
gcc8.3.1目前有133个错误代码
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int ii;
for(ii=0;ii<150;ii++) // gcc8.3.1一共有133个错误代码。
{
cout << ii << ":" << strerror(ii) << endl;
}
}
perror()
perror() 在<stdio.h>
中声明,用于在控制台显示最近一次系统错误的详细信息,在实际开发中,服务程序在后台运行,通过控制台显示错误信息意义不大。(对调试程序略有帮助)
void perror(const char *s);
注意
1)调用库函数失败不一定会设置errno
并不是全部的库函数在调用失败时都会设置errno的值,以man手册为准(一般来说,不属于系统调用的函数不会设置errno,属于系统调用的函数才会设置errno)。什么是系统调用?百度“库函数和系统调用的区别”。
2)errno不能作为调用库函数失败的标志
errno的值只有在库函数调用发生错误时才会被设置,当库函数调用成功时,errno的值不会被修改,不会主动的置为 0。
在实际开发中,判断函数执行是否成功还得靠函数的返回值,只有在返回值是失败的情况下,才需要关注errno的值。
#include <iostream>
#include <cstring> // strerror()函数需要的头文件。
#include <cerrno> // errno全局变量的头文件。
#include <sys/stat.h> // mkdir()函数需要的头文件。
using namespace std;
int main()
{
int iret = mkdir("/tmp/aaa/bb/cc/dd", 0755);
if (iret != 0)
{
cout << "iret=" << iret << endl;
cout << errno << ":" << strerror(errno) << endl;
perror("调用mkdir(/tmp/aaa/bb/cc/dd)失败");
}
iret = mkdir("/tmp/dd", 0755);
if (iret != 0)
{
cout << "iret=" << iret << endl;
cout << errno << ":" << strerror(errno) << endl;
perror("调用mkdir(/tmp/dd)失败");
}
}