一.宏日志类logger.hpp
cpp
#ifndef __M_LOG_H__
#define __M_LOG_H__
#include <iostream>
#include <ctime>
#define DBG_LEVEL 0
#define INF_LEVEL 1
#define ERR_LEVEL 2
#define DEFAULT_LEVEL DBG_LEVEL
#define LOG(lev_str, level, format, ...) {\
if (level >= DEFAULT_LEVEL) {\
time_t t = time(nullptr);\
struct tm* ptm = localtime(&t);\
char time_str[32];\
strftime(time_str, 31, "%H:%M:%S", ptm);\
printf("[%s][%s][%s:%d]\t" format "\n", lev_str, time_str, __FILE__, __LINE__, ##__VA_ARGS__);\
}\
}
#define DLOG(format, ...) LOG("DBG", DBG_LEVEL, format, ##__VA_ARGS__)
#define ILOG(format, ...) LOG("INF", INF_LEVEL, format, ##__VA_ARGS__)
#define ELOG(format, ...) LOG("ERR", ERR_LEVEL, format, ##__VA_ARGS__)
#endif
默认情况下依次打印 日志等级,时间,文件名,行号,并且额外提供了C风格的格式化输出。
demo:
cpp
DLOG("This is a debug message: value = %d", 42);
ILOG("This is an info message.");
ELOG("This is an error message.");
cpp
[DBG][12:34:56][main.cpp:10] This is a debug message: value = 42
[INF][12:34:56][main.cpp:11] This is an info message.
[ERR][12:34:56][main.cpp:12] This is an error message.
二.公共工具类helper.hpp
SqliteHelper
cpp
class SqliteHelper
{
private:
std::string _dbfile;
sqlite3 *_handler;
public:
typedef int (*SqliteCallback)(void *, int, char **, char **);
SqliteHelper(const std::string &dbfile)
: _dbfile(dbfile), _handler(nullptr) {}
bool open(int safe_leve = SQLITE_OPEN_FULLMUTEX)
{
// int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs );
int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_leve, nullptr);
if (ret != SQLITE_OK)
{
ELOG("创建/打开sqlite数据库失败: %s", sqlite3_errmsg(_handler));
return false;
}
return true;
}
bool exec(const std::string &sql, SqliteCallback cb, void *arg)
{
// int sqlite3_exec(sqlite3*, char *sql, int (*callback)(void*,int,char**,char**), void* arg, char **err)
int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
if (ret != SQLITE_OK)
{
ELOG("%s \n语句执行失败: %s", sql.c_str(), sqlite3_errmsg(_handler));
return false;
}
return true;
}
void close()
{
if (_handler)
sqlite3_close_v2(_handler);
}
};
用于简化SQLite数据库的操作。SQLite是一个轻量级的数据库,它将整个数据库存储在一个单一的磁盘文件中,不需要一个独立的服务器进程。
这个类封装了一些基本的SQLite操作,使得在C++程序中使用SQLite数据库更加方便。
std::string _dbfile
:用于存储 数据库文件的路径+数据库名称。
sqlite3 *_handler
:是一个指向 sqlite3
结构的指针,用于操作数据库(管理句柄)。
bool open(int safe_level = SQLITE_OPEN_FULLMUTEX)
:用于打开或创建数据库。默认使用 SQLITE_OPEN_FULLMUTEX
标志来确保线程安全。
bool exec(const std::string &sql, SqliteCallback cb, void arg)***:用于执行SQL语句。可以传递一个回调函数和一个参数(一般用来保存查询结果)给这个函数。
StrHelper
cpp
class StrHelper
{
public:
static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &ret)
{
size_t start = 0;
size_t pos = std::string::npos;
// 123...123.
while ((pos = str.find(sep, start)) != std::string::npos) // 没有找到分隔符
{
if (pos == start)
{
start = pos + sep.size();
continue;
}
ret.push_back(std::move(str.substr(start, pos - start)));
start = pos + sep.size();
}
// 处理最后一个分隔符后的数据
if (start < str.size())
ret.push_back(std::move(str.substr(start)));
return ret.size();
}
};
static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &ret)
:这是一个静态成员函数,用于分割字符串。它接收三个参数:const std::string &str
:要分割的原始字符串。const std::string &sep
:分隔符字符串。std::vector<std::string> &ret
:用于存储分割结果的字符串向量。
函数逻辑
-
size_t start = 0;
:定义一个变量start
来记录当前搜索的起始位置。 -
size_t pos = std::string::npos;
:定义一个变量pos
来存储分隔符的位置,初始值为std::string::npos
,表示未找到分隔符。 -
while ((pos = str.find(sep, start)) != std::string::npos)
:使用find
方法循环查找字符串str
中的分隔符sep
。如果找到了分隔符,pos
将被更新为分隔符的索引。 -
if (pos == start)
:如果分隔符紧跟在start
之后,跳过这个空字符串,更新start
为分隔符之后的位置,并继续循环(跳过连续分隔符) -
ret.push_back(std::move(str.substr(start, pos - start)));
:将从start
到pos
的子字符串添加到结果向量ret
中。使用std::move
可以避免复制,提高效率。 -
start = pos +
sep.size();
:更新start
为下一个搜索的起始位置。 -
if (start < str.size())
:处理最后一个分隔符之后剩余的字符串部分,将其添加到结果向量中。 -
return ret.size();
:返回分割后得到的子字符串数量。
demo
cpp
std::vector<std::string> result;
size_t count = StrHelper::split("one,two,three,four", ",", result);
for (const auto &part : result) {
std::cout << part << std::endl;
}
// 输出:
// one
// two
// three
// four
UUIDHelper
cpp
class UUIDHelper
{
public:
// 8-4-4----4-12
static std::string uuid()
{
// 重载了operator()
// 1.用硬件随机数作为随机数种子
// 2.生成伪随机数[0,255]
// 3.转化为16进制,并用-分割
// 4.生成16位的序号,便于观察
std::random_device rd;
std::mt19937_64 mt(rd()); // 伪随机数
std::uniform_int_distribution<int> distribution(0, 255);
std::stringstream sstream;
for (int i = 0; i < 8; ++i)
{
int rnum = distribution(mt);
sstream << std::setw(2) << std::setfill('0') << std::hex << rnum;
if (i == 3 || i == 5 || i == 7)
sstream << "-";
}
static std::atomic<int> cnt(1); // 全局唯一
// 每次获取uuid,cnt都自增1
size_t num = cnt.fetch_add(1); // 8字节,每次取出1字节
for (int i = 7; i >= 0; --i)
{
sstream << std::setw(2) << std::setfill('0') << std::hex << ((num >> i * 8) & 0xff);
if (i == 6)
sstream << "-";
}
return sstream.str();
}
};
随机数种子和伪随机数:
使用 std::random_device 作为随机数种子,确保每次生成的UUID都是不同的。
使用 std::mt19937_64 作为伪随机数生成器,它基于Mersenne Twister算法。
生成随机数:
使用 std::uniform_int_distribution<int> 来生成一个在0到255范围内的均匀分布的整数。
字符串流操作:使用 std::stringstream 来构建最终的UUID字符串。
生成UUID的主体部分:
循环8次,每次生成一个两位的16进制数,并根据UUID的格式在第4、6、8位后添加短横线。
全局计数器:
使用 std::atomic<int> 定义一个全局计数器 cnt,确保即使在多线程环境下也能安全地递增。
生成UUID的序号部分:
每次调用 uuid 函数时,cnt 递增,并将其转换为16进制形式,然后插入到UUID字符串的最后12位。
返回结果:
最终的UUID字符串通过 sstream.str() 获取,并作为函数的返回值。

FileHelper
cpp
class FileHelper
{
private:
std::string _filename;
public:
FileHelper(const std::string &filename) : _filename(filename) {}
bool exists()
{
struct stat st;
int ret = stat(_filename.c_str(), &st);
if (ret < 0)
{
ELOG("%s 该文件/目录不存在", _filename.c_str());
return false;
}
return true;
}
size_t size()
{
struct stat st;
int ret = stat(_filename.c_str(), &st);
if (ret < 0)
{
ELOG("%s 文件不存在", _filename.c_str());
return 0;
}
return st.st_size;
}
bool read(std::string &text)
{
size_t sz = size();
text.resize(sz);
return read(&text[0], 0, sz);
}
bool read(char *buffer, size_t offset, size_t len)
{
// 1.打开文件
// 2.调整偏移量
// 3.读取数据
// 4.关闭文件
std::ifstream ifs(_filename, std::ios::binary | std::ios::in);
if (!ifs.is_open())
{
ELOG("%s 打开文件失败", _filename.c_str());
return false;
}
ifs.seekg(offset, std::ios::beg);
ifs.read(buffer, len);
if (!ifs.good())
{
ELOG("%s 文件读取失败", _filename.c_str());
ifs.close();
return false;
}
ifs.close();
return true;
}
bool write(const std::string &text)
{
return write(text.c_str(), 0, text.size());
}
bool write(const char *buf, size_t offset, size_t len)
{
std::fstream fs(_filename, std::ios::binary | std::ios::in | std::ios::out);
if (!fs.is_open())
{
ELOG("%s 打开文件失败", _filename.c_str());
return false;
}
fs.seekp(offset, std::ios::beg);
fs.write(buf, len);
if (!fs.good())
{
ELOG("%s 文件写入失败", _filename.c_str());
fs.close();
return false;
}
fs.close();
return true;
}
bool rename(const std::string &nname)
{
return (::rename(_filename.c_str(), nname.c_str()) == 0);
}
//---------------静态成员函数,创建/移除 目录和文件---------------
static bool createFile(const std::string &filename)
{
std::ofstream ofs(filename.c_str(), std::ios::binary | std::ios::out | std::ios::app);
if (!ofs.is_open())
{
ELOG("%s 打开文件失败", filename.c_str());
return false;
}
ofs.close();
return true;
}
static bool removeFile(const std::string &filename)
{
int ret = ::remove(filename.c_str());
if (ret < 0 && errno != ENOENT)
{
ELOG("%s 删除文件失败", filename.c_str());
return false;
}
return true;
}
static bool createDir(const std::string &path)
{
//"aaa/bbb/ccc/ddd" 先依次创建上级目录
size_t begin = 0, pos = 0;
while (pos = path.find('/', begin))
{
if (pos == std::string::npos)
{
std::string dir = path.substr(0);
::mkdir(dir.c_str(), 0775);
return true;
}
std::string dir = path.substr(0, pos);
int ret = ::mkdir(dir.c_str(), 0775);
if (ret < 0 && errno != EEXIST)
{
ELOG("%s 创建目录失败", dir.c_str());
ELOG("错误原因: %s", strerror(errno));
return false;
}
begin = pos + 1;
}
return true;
}
static bool removeDir(const std::string &path)
{
std::string str = " rm -rf " + path;
return system(str.c_str()) == 0;
}
static std::string getParentDirName(const std::string &filename)
{
size_t pos = filename.rfind('/');
if (pos == std::string::npos)
{
return "./";
}
std::string path = filename.substr(0, pos);
return path;
}
};
封装了一些常用的文件操作,如检查文件是否存在、获取文件大小、读取和写入文件内容、重命名文件、以及创建和删除文件和目录等。
成员函数
bool exists()
:检查文件或目录是否存在。size_t size()
:获取文件的大小。bool read(std::string &text)
:读取整个文件内容到字符串。bool read(char *buffer, size_t offset, size_t len)
:从文件中读取指定长度的数据到缓冲区。bool write(const std::string &text)
:将字符串内容写入文件。bool write(const char *buf, size_t offset, size_t len)
:将缓冲区的内容写入文件的指定位置。bool rename(const std::string &nname)
:重命名文件。
静态成员函数
static bool createFile(const std::string &filename)
:创建一个新文件。static bool removeFile(const std::string &filename)
:删除一个文件。static bool createDir(const std::string &path)
:递归创建目录。static bool removeDir(const std::string &path)
:删除目录及其内容。static std::string getParentDirName(const std::string &filename)
:获取文件的父目录名称。