当前位置: 首页 > news >正文

第二章-续:辅助功能

目录

第一节:UUIDHelper

第二节:StrHelper

第三节:单元测试

        3-1.日志宏测试

        3-2.SqliteHelper测试

                3-2-1.创建表测试 

                 3-2-2.添加数据测试

                3-2-3.修改数据测试

                3-2-4.删除数据测试

                3-2-5.获取数据测试

        3-3.FileHelper测试

                3-3-1.创建/打开文件测试

                3-3-2.写数据测试

                3-3-3.读取数据测试

        3-4.UUIDHelper测试

        3-5.StrHelper测试

下期预告:


第一节:UUIDHelper

        UUIDHelper就是随机id生成器。

        仍然是在mq_helper.hpp文件中,定义UUIDHelpr类:

class UUIDHelper
{};

        它只对外提供生成随机id的函数:

    static std::string uuid()
    {
        // std::random_device 生成机器随机数,效率低
        std::random_device rd;
        
        // std::mt19937_64 生成伪随机数,以rd为种子,效率高
        std::mt19937_64 rand(rd());
    
        // 生成0~255之间的随机数
        std::uniform_int_distribution<int> distr(0,255);

        // 生成前缀随机数
        std::stringstream ss;
        for(int i = 0;i < 8;i++)
        {
            ss << std::setw(2) << std::setfill('0') << std::hex << distr(rand);
            if(i == 3 || i == 5 || i == 7)
                ss<<"-";
        }
        
        // 生成序列号
        static std::atomic<size_t> sep(1);
        size_t number = sep.fetch_add(1); // 先获取sep当前值,sep再自增1
        for(int i = 7;i >= 0;i--)
        {
            ss << std::setw(2) << std::setfill('0') << std::hex << ((number>>(i*8)) & 0xff);
            if(i == 6)
                ss << "-";
        }
        return ss.str();
    }

        前缀随机数由8个int类型的随机数组合而成,共32字节,因为最大的随机数是255,转成十六进制后最多两位,一位又以"0"填充,这样让每个前缀都是16位。

        序列号由一个静态原子变量维护,number是一个4字节(32比特)的数据,每次将序列号的8个比特位转成两位的十六进制,处理8次,就可以正好处理了32比特。只要程序不关闭,每次的序列号肯定是不同的,这样即使前缀相同,它们的识别码也不相同。

第二节:StrHelper

        StrHelper也只提供一个对外的接口,它的作用是分割字符串,这在交换机的主题模式的消息审核中十分重要:

class StrHelper
{
public:
// 截取sep之间、开头与sep、sep与结尾之间的字符串
    static size_t split(const std::string& str,const std::string& sep,std::vector<std::string>& result)
    {
        // news.music.#.pop
        // 截取到news、music、#、pop
        size_t pos = 0; // 每次查找到的子串起始位置
        size_t idx = 0; // 每次查找的起始位置
        while(idx < str.size())
        {
            pos = str.find(sep,idx);
            if(pos == std::string::npos) // 没找到sep
            {
                // 从查找位置起始截取到末尾
                result.push_back(str.substr(idx));
                // 返回找到的子串数量
                return result.size();
            }
            else
            {
                if(pos == idx) // 空串不用截取
                {
                    idx=pos+sep.size();
                    continue;
                }
                result.push_back(str.substr(idx,pos-idx));
                idx=pos+sep.size();
            }
        }
        return result.size();
    }
};

第三节:单元测试

        所有的单元测试都在mqtest目录下进行。

        3-1.日志宏测试

        创建一个名为mq_log_test.cc的文件,并包含mq_logger.hpp文件:

#include "../mqcommon/mq_logger.hpp"

         再包含<string>作为可变参数:

#include <string>

        最后完成main函数:

#include "../mqcommon/mq_logger.hpp"
#include <string>
int main()
{
    std::string msg = "Hello!";
    LOG("日志宏测试:%s",msg.c_str());
    return 0;
}

        再创建makefile文件进行编译:

mq_log_test:mq_log_test.cc
	g++ -std=c++14 $^ -o $@

         运行结果:

  

        时间正确、文件正确、行号正确、打印内容正确。

         日志宏的功能正常。

        3-2.SqliteHelper测试

        创建一个名为mq_sqlite_test.cc的文件,打开并包含头文件:

#include "../mqcommon/mq_helper.hpp"

                3-2-1.创建表测试 

        先测试创建表的功能:

int main()
{
    // 创建sqlite数据库文件的管理句柄
    zd::SqliteHelper shelper("./meta.bd");
    // 使用句柄创建文件
    shelper.open();
    // 使用句柄创建一张名为 student 的数据表
        // 1.编辑创建表的指令
        // sn:学号  name:姓名  age:年龄
        // primary key:表示这个数据作为键,一个表内不允许出现相同键的数据
    char Create_Table[] = "create table if not exists student(sn int primary key ,name varchar(32),age int);";
        // 2.使用exec接口执行这个指令
    shelper.exec(Create_Table,nullptr,nullptr);
    return 0;
}

        将上述代码进行编译,因为使用了sqlite库,所以还要链接该库:

mq_sqlite_test:mq_sqlite_test.cc
	g++ -std=c++14 $^ -o $@ -lsqlite3

        执行后应当在当前目录出现meta.bd文件:

        

        然后使用sqlite指令打开meta.bd文件: 

sqlite3 meta.bd

        会出现输入指令的提示:

  

        输入 .table 会出现一个名为 student 的表,这就是创建的表:

  

        此时表中是没有数据的,我们再完善代码。

                 3-2-2.添加数据测试

        先把 meta.bd 删除,因为只要之前的代码不变,它还是会被创建出来。

        然后在main函数中继续添加如下代码:

    // 使用句柄向表 student 添加数据
        // 1.构建插入数据的指令
    char Insert_Data[] = "insert into student values(1,'李华',18),(2,'吴凡',17),(3,'Eric',19);";
        // 2.使用exec接口执行这个指令
    shelper.exec(Insert_Data,nullptr,nullptr);

        重新编译并执行,执行完毕后重复上述步骤,使用sqlite指令打开meta.bd文件后,输入指令:select * from student;

        在屏幕上打印表中所有数据:

                                                          

        可以看见之前的数据都添加成功了。

                3-2-3.修改数据测试

        还是先删除meta.bd文件,在mian中添加如下代码:

    // 使用句柄向表 student 修改数据
        // 1.构建修改数据的指令:吴凡 改名 吴不凡
    char Modify_Data[] = "update student set name='吴不凡' where sn=2;";
        // 2.使用exec接口执行这个指令
    shelper.exec(Modify_Data,nullptr,nullptr);

        编译并执行后打印表的内容:

                                                        

        吴凡 同学就变成 吴不凡 同学了。

                3-2-4.删除数据测试

        删除meta.bd文件,在main中添加如下代码:

    // 使用句柄从表 student 删除数据
    char Delete_Data[] = "delete from student where sn=3;";
    shelper.exec(Delete_Data,nullptr,nullptr);

        编译、执行、查看表内容:

                                ​​​​​​​        ​​​​​​​                ​​​​​​​

        毕业的 Eric 同学就从数据库中删除了。

                3-2-5.获取数据测试

        获取表中的数据时,它是以数组的形式返回,每个元素就是一条数据的一个属性,所以先创建一个std::vecotr<std::string>类型的变量,再设置一个回调函数,回调函数的作用是把数据的属性存储到变量中:

// 回调函数
int select_stu_callback(void* arg,int col_count,char** result,char** files_name)
{
    std::vector<std::string> *arry = (std::vector<std::string>*)arg;
    std::string col_msg = result[0]; // 一条数据的sn
    col_msg+=result[1]; // 一条数据的name
    col_msg+= result[2]; // 一条数据的age
    arry->push_back(col_msg);

    return 0; // 返回0表示成功了
}
    // 使用句柄从表 student 获取数据
    std::vector<std::string> datas;
    char Select_Data[] = "select sn,name,age from student;";
    shelper.exec(Select_Data,select_stu_callback,&datas);

    // 打印获取到的数据 
    for(auto& data:datas)
    {
        std::cout << data << std::endl;
    }

        执行结果:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​         

        回调函数只处理了一条数据,那么为什么打印出来的是两条数据的内容呢?因为每有一条数据,回调函数就会被调用一次,它会一个一个的处理每条数据。

        至此,SqliteHelper的测试就完毕了。 

        3-3.FileHelper测试

        创建一个名为mq_file_test.cc的文件,打开并包含头文件:

#include "../mqcommon/mq_helper.hpp"

                3-3-1.创建/打开文件测试

#include "../mqcommon/mq_helper.hpp"

int main()
{
    // 创建文件管理句柄
    zd::FileHelper fhelper("./data/file");
    // 使用句柄创建/打开文件
        // 1.创建文件之前,先创建文件所需目录
            // 1.1.获取目录
    std::string dir = fhelper.parentDirectory();
            // 1.2.创建目录
    zd::FileHelper::createDirectory(dir);
        // 2.目录创建完成才能创建文件
    fhelper.createFile();

    return 0;
}

        编译上述代码并执行:

mq_file_test:mq_file_test.cc
	g++ -std=c++14 $^ -o $@

        执行后当前目录应该出现data目录,data下有一个名为file的文件:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​

                3-3-2.写数据测试

        main函数新增以下代码:

    // 使用句柄向文件写入数据
    std::string data = "helloworld";
    fhelper.write(data);

        编译并执行后,打开file文件,里面已经有了写入的内容:

                                

        然后再测试一下向文件中间写入数据,因为每次打开/创建文件时,文件内容都会被清空,所以不需要每次都删除文件了。

        新增的代码如下:

    // 使用句柄向文件中间写入数据
    std::string data1 = "12345";
    fhelper.write(data1.c_str(),5,data1.size());

         编译执行后,world正好5个字符就被覆盖了:

                                        

                3-3-3.读取数据测试

        main函数新增代码:

    // 使用句柄从文件读取数据
    std::string data2;
    fhelper.read(data2);

    // 使用句柄从文件中间读取数据
    std::string data3;
    data3.resize(5);
    fhelper.read(&data3[0],5,5); // 从第六个字符开始读取,读取5个

    // 打印读取到的数据
    LOG("起始读取:%s",data2.c_str());
    LOG("中间读取:%s",data3.c_str());

         data2应该读取到整个文件的内容,而data3应该读取到文件最后的"12345",执行结果:

        FileHelper的其他功能函数可以自己去测试,这里不再演示了,唯一需要注意的是重命名函数的参数需要带上路径,不然重命名后的文件就在mqtest路径下了。

        3-4.UUIDHelper测试

        UUIDHelper很好测试,只需要调用函数,打印出得到的随机id即可:

#include "../mqcommon/mq_helper.hpp"
int main()
{
    for(int i = 0;i < 100;i++)
    {
        LOG("获得的uuid: %s",zd::UUIDHelper::uuid().c_str());
    }
    return 0;
}

        执行结果:

        序列号也是正常的。

        3-5.StrHelper测试

        它的测试也很简单:

#include "../mqcommon/mq_helper.hpp"
void Print(std::vector<std::string> words)
{
    for(const auto& word:words)
    {
        std::cout << word << " ";
    }
    std::cout << std::endl;
}

int main()
{
    std::vector<std::string> v1;
    std::vector<std::string> v2;
    std::vector<std::string> v3;
    std::vector<std::string> v4;

    zd::StrHelper::split("news.music.pop",".",v1);
    zd::StrHelper::split("news.music...pop",".",v2);
    zd::StrHelper::split("news.music.pop...",".",v3);
    zd::StrHelper::split("..news.music.pop",".",v4);

    Print(v1);
    Print(v2);
    Print(v3);
    Print(v4);
    
    return 0;
}

         结果也是符合预期的:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        

        至此,五个辅助代码都测试完毕了。

下期预告:

        下一章将完成最后一个辅助类——工作线程池。 

相关文章:

  • EX_25/2/22
  • 第5章 软件工程(二)
  • Crack SmartGit
  • vue3中Watch和WatchEffect的用法和区别
  • 调用DeepSeek API 增强版纯前端实现方案,支持文件上传和内容解析功能
  • sam2 windows 编译安装
  • springboot单机支持1w并发,需要做哪些优化
  • 国产编辑器EverEdit - 如何在EverEdit中创建工程?
  • 使用Uni-app实现语音视频聊天(Android、iOS)
  • 漏洞文字版表述一句话版本(漏洞危害以及修复建议),通常用于漏洞通报中简洁干练【持续更新中】
  • JavaWeb-在idea中配置Servlet项目
  • Linux下网络运维命令总结
  • Linux设备驱动开发-SPI驱动开发详解(包含设备树处理详细过程)
  • AR技术下的电商:虚拟试穿/试用/试戴成新风尚
  • Linux系统需要学习的内容和学习计划
  • 网络运维学习笔记(DeepSeek优化版)001网工初级(HCIA-Datacom与CCNA-EI)网络架构与通信原理
  • touchgfx的工作机制
  • Redis基础认知
  • 第四篇:微信小程序网络请求与API调用:实现数据交互
  • Spring Boot 中为什么 需要限流、降级和熔断?
  • 智能建网站/seo搜索引擎优化推荐
  • WordPress做头部的插件/seo推广网址
  • 商业活动的网站建设/外贸平台app
  • 网站上线如何做压力测试/网页设计html代码大全
  • 网站建设图片/seo推广是什么意思呢
  • ps模板网站/创建网站的流程