作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。
专栏简介:本文收录于 C++项目——基于多设计模式下的同步与异步日志系统
相关专栏推荐:C语言初阶系列、C语言进阶系列 、C++系列、数据结构与算法、Linux
在项目中,我们时常会需要用到一些与业务无关的功能,如:获取系统时间、创建目录、获取路径等。我们将这些零碎的功能接口提前完成,以便于项目中会用到。
实用工具类主要包含以下功能:
获取系统时间
;判断文件是否存在
;获取文件所在路径
;创建文件所在目录
;我们将获取系统时间的接口单独封装在一个Date类
中。获取系统时间,我们可以使用库函数time
来实现。
C语言中的time
函数是一个用于获取当前系统时间的标准库函数,它定义在
头文件中。time
函数通常返回自1970年1月1日
以来经过的秒数,这被称为Unix时间戳
(或Epoch时间)。它的函数原型如下:
time_t time(time_t *tloc);
time_t
是一种数据类型,通常是一个整数类型(如long),用来存储时间值;tloc
是一个指向time_t
类型的指针
,用于存储获取的时间值。你可以将它设置为NULL
,如果你不需要获取时间值的副本;time
函数返回一个表示当前时间的时间戳
,单位是秒
。如果传递了非空的tloc
指针,它还会将时间戳的副本存储在tloc
指向的地址中,以便你可以稍后使用。
#include
#include
class Date
{
public:
static size_t getTime()
{
return (size_t)time(nullptr);
}
};
判断文件是否存在、获取文件所在路径、创建目录这三个功能都与文件相关,因此我们将三者在一个File类
中实现。
实现思路:
路径字符串
,表示所要判断的文件的路径;bool
类型,若该文件存在则返回true
;stat
来实现;认识stat
函数之前我们首先认识一下struct stat
类型。
在C语言中,struct stat
是一个用于表示文件或文件系统对象属性
的结构体类型。这个结构体通常用于与文件和目录
相关的操作,例如获取文件的大小、访问权限、最后修改时间等信息。struct stat
类型的定义通常由操作系统提供,因此其具体字段可能会因操作系统而异。
以下是一个典型的struct stat
结构体的字段,尽管具体字段可能会因操作系统而异:
struct stat {
dev_t st_dev; // 文件所在设备的ID
ino_t st_ino; // 文件的inode号
mode_t st_mode; // 文件的访问权限和类型
nlink_t st_nlink; // 文件的硬链接数量
uid_t st_uid; // 文件的所有者的用户ID
gid_t st_gid; // 文件的所有者的组ID
off_t st_size; // 文件的大小(以字节为单位)
time_t st_atime; // 文件的最后访问时间
time_t st_mtime; // 文件的最后修改时间
time_t st_ctime; // 文件的最后状态改变时间
blksize_t st_blksize; // 文件系统I/O操作的最佳块大小
blkcnt_t st_blocks; // 文件占用的块数
};
struct stat
结构体中的这些字段提供了关于文件或目录的各种信息。不同的操作系统可能会提供额外的字段,或者字段的意义可能会有所不同。
stat
函数用于获取与指定路径名相关联的文件或目录的属性
,并将这些属性填充到一个struct stat
结构体中。以下是stat
函数的函数原型:
int stat(const char *pathname, struct stat *statbuf);
pathname
是要获取属性的文件或目录的路径名;statbuf
是一个指向struct stat
结构体的指针,用于存储获取到的属性信息;stat
函数返回一个整数值,如果操作成功,返回0
;如果出现错误,返回-1
,并设置errno
全局变量以指示错误的类型。注意需要包含头文件
和
来使用stat
函数。
static bool exists(const std::string &pathname)
{
struct stat st;
if (stat(pathname.c_str(), &st) < 0) // 打开文件失败则代表文件不存在
{
return false;
}
return true;
}
假设存在文件“user/aaa/bbb/ccc/test.cc”
,我们需要获取文件test.cc
所在的路径即"user/aaa/bbb/ccc"
。
实现这个功能我们将会用到库函数find_last_of
。
C++标准库中的find_last_of
函数是用于在字符串中查找指定字符集中最后一个出现的字符
,并返回其位置或索引。这个函数通常用于字符串操作,允许你在字符串中查找某些字符集的最后一个匹配字符。
函数格式如下:
size_t find_last_of(const string& str, size_t pos = string::npos) const;
size_t find_last_of(const char* s, size_t pos = string::npos) const;
size_t find_last_of(const char* s, size_t pos, size_t n) const;
其中,
str
是要搜索的字符串;s
是要查找的字符集;pos
是可选的参数,用于指定搜索的起始位置,默认为string::npos
,表示从字符串的末尾开始向前搜索。这个函数返回匹配字符集中任何字符的最后一个位置的索引,如果未找到匹配字符,则返回string::npos
。需要注意的是,返回的索引是从0
开始的。
就以文件“user/aaa/bbb/ccc/test.cc”
为例,要想获取文件路径,我们只需要找到最后一个‘/
或者 \\
所在位置,并将在这之前的内容全部返回即可。若不存在路径分隔符/
或者 \\
,则证明该文件在当前目录,返回.
即可。
static std::string path(const std::string &pathname)
{
size_t pos = pathname.find_last_of("/\\");
if (pos == std::string::npos)
return ".";
return pathname.substr(0, pos + 1);
}
以文件路径'' user/aaa/bbb/ccc/test.cc ''
,给函数传递该路径字符串,函数的任务是依次创建目录user
、aaa
、bbb
、ccc
。
需要注意的小细节是,每次要创建一个目录时,都要判断该目录是否存在,使用之前实现的exists
函数即可。
我们依靠系统调用mkdir
来完成目录的创建,首先来认识一下mkdir
。
mkdir
函数是一个系统调用,用于在文件系统中创建新的目录(文件夹)。它通常用于在文件系统中创建一个新的目录,以便存储文件或其他目录。
函数原型如下:
#include
#include
int mkdir(const char *pathname, mode_t mode);
pathname
是一个字符串,表示要创建的目录的路径。这个路径可以是相对路径
或绝对路径
;
mode
是一个权限掩码
,用于指定新目录的权限。这个权限掩码通常是八进制
数;
mkdir
函数的功能是创建一个新的目录,并根据指定的权限设置来设置目录的权限。如果成功创建目录,函数将返回0
,否则返回-1
,并设置 errno
变量以指示错误的原因。
在 C++ 中,find_first_of
是字符串(std::string)和其他序列容器中的成员函数,用于在目标字符串中查找第一个匹配源字符串中任何字符的位置
。它的功能是找到目标字符串中的任何一个字符在源字符串中第一次出现的位置。
函数类型如下:
size_t find_first_of(const std::basic_string& str, size_t pos = 0) const;
size_t find_first_of(const CharT* s, size_t pos = 0) const;
size_t find_first_of(const CharT* s, size_t pos, size_t n) const;
size_t find_first_of(CharT ch, size_t pos = 0) const;
str
:一个字符串,表示源字符串,函数将在目标字符串中查找源字符串中的任何字符;s
:一个字符数组或 C 字符串,表示源字符序列,函数将在目标字符串中查找数组中的任何字符;ch
:一个字符,表示要查找的字符;pos
:可选参数,表示开始查找的位置。默认为0,即从字符串的开头开始查找;n
:可选参数,与 s 一起使用,表示要查找的字符数量;find_first_of
函数返回目标字符串中第一个匹配源字符序列中任何字符的位置(索引),如果没有找到匹配的字符,则返回 std::string::npos
。
static void createDirectory(const std::string pathname)
{
size_t pos = 0, idx = 0;
while(idx < pathname.size())
{
pos = pathname.find_first_of("/\\", idx);
if(pos == std::string::npos)
{
mkdir(pathname.c_str(), 0777);
}
std::string parent_dir = pathname.substr(0, pos + 1);
if(exists(parent_dir) == true) // 判断该文件是否已经存在
{
idx = pos + 1;
continue;
}
mkdir(parent_dir.c_str(), 0777);
idx = pos + 1;
}
}
在项目实现中,我们最好使用自己的命名空间。我们将各个类整体放入LOG
(名称自行决定)命名空间中的util
命名空间中。
#ifndef __M_UTIL_H__
#define __M_UTIL_H__
/*
使用工具类实现:
1. 获取系统时间
2. 判断文件是否存在
3. 获取文件所在路径
4. 创建文件所在目录
*/
#include
#include
#include
namespace LOG
{
namespace util
{
class Date
{
public:
static size_t getTime()
{
return (size_t)time(nullptr);
}
};
class File
{
public:
static bool exists(const std::string &pathname)
{
struct stat st;
if (stat(pathname.c_str(), &st) < 0) // 打开文件失败则代表文件不存在
{
return false;
}
return true;
}
static std::string path(const std::string &pathname)
{
size_t pos = pathname.find_last_of("/\\");
if (pos == std::string::npos)
return ".";
return pathname.substr(0, pos + 1);
}
static void createDirectory(const std::string pathname)
{
size_t pos = 0, idx = 0;
while(idx < pathname.size())
{
pos = pathname.find_first_of("/\\", idx);
if(pos == std::string::npos)
{
mkdir(pathname.c_str(), 0777);
}
std::string parent_dir = pathname.substr(0, pos + 1); // 判断该文件是否已经存在
if(exists(parent_dir) == true)
{
idx = pos + 1;
continue;
}
mkdir(parent_dir.c_str(), 0777);
idx = pos + 1;
}
}
};
}
}
#endif