首页 > 编程语言 >spdlog一个非常好用的C++日志库(十): 十六进制输出spdlog::to_hex

spdlog一个非常好用的C++日志库(十): 十六进制输出spdlog::to_hex

时间:2024-11-10 09:46:38浏览次数:3  
标签:std 十六进制 38 hex spdlog line size

目录

1.引言

2.spdlog::to_hex用法

3.spdlog::to_hex实现原理

4.总结


1.引言

        在平时调试网络程序时,多数都会用到wireshark抓包工具,在查看某个包的数据都是按照下面这样格式显示的:

那么它是怎么做到的呢?其实spdlog的to_hex也能做到这一点,下面就来介绍它的用法和原理。

2.spdlog::to_hex用法

  spdlog::to_hex 这个方法是用于将个字节序列转换为十六进制字符串的功能。它自带的几种十六进制显示格式符号:

符号含义例子
{:X}以大写字母打印十六进制数,默认是小写显示0000: 09 0A 0B 0C FF FF
{:s}不带间隔符显示0000: 090a0b0cffff
{:n}不要将输出分割成行09 0a 0b 41 0d 4b ff ff
{:a}如果未设置 :n,则显示 ASCII 字符(对于可打印的 ASCII 字符)0000: 09 0a 0b 41 0d 4b ff ff  ...A.K..
{:p}不要在每行开始时打印位置09 0a 0b 41 0d 4b ff ff

        此外,你还可以指定每行显示的字节数,例如,通过 {:Xspn} 这样的格式化字符串,to_hex的最后一个参数的 32 表示每行显示 32个字节。这些选项可以混合使用,以满足不同的输出需求。

        其次在使用的spdlog::to_hex的时候,内容必须是字节流或字符串。

        下面是几个示例:

void  testLog()
{
	std::vector<unsigned char> v{ 9, 0xa, 0xb, 0xc, 0xff, 0xff };
	UVLOG_INFO("1: {}", spdlog::to_hex(v)); //普通十六进制输出: 0000: 09 0a 0b 0c ff ff

	std::vector<unsigned char> v1{ 9, 0xa, 0xb, 0xc, 0xff, 0xff };
	UVLOG_INFO("2: {:X}", spdlog::to_hex(v1)); //十六进制大写输出 : 0000: 09 0A 0B 0C FF FF
	UVLOG_INFO("21: {:s}", spdlog::to_hex(v1)); //十六进制大写输出 : 0000: 090a0b0cffff

	std::vector<unsigned char> v2{ 9, 0xa, 0xb, 0xc, 0xff, 0xff };
	UVLOG_INFO("3: {:sX}", spdlog::to_hex(v2)); //十六进制不间隔输出:0000: 090A0B0CFFFF

	std::vector<unsigned char> v3{ 9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff };
	UVLOG_INFO("4: {:Xsa}", spdlog::to_hex(v3, 8));//十六进制不间隔大写带ascii输出:0000: 090A0B410C4BFFFF  ...A.K..
	UVLOG_INFO("41: {:a}", spdlog::to_hex(v3, 8));//十六进制带ascii输出:0000: 09 0a 0b 41 0c 4b ff ff  ...A.K..
	UVLOG_INFO("42: {:na}", spdlog::to_hex(v3, 8));//十六进制不带ascii输出:09 0a 0b 41 0c 4b ff ff

	std::vector<unsigned char> v4{ 9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff };
	UVLOG_INFO("5: {:Xsa}", spdlog::to_hex(v4, 10)); //0000: 090A0B410C4BFFFF  ...A.K..

	UVLOG_INFO("6: {:Xs}", spdlog::to_hex(v4, 10)); //0000: 090A0B410C4BFFFF
	UVLOG_INFO("7: {:Xsa}", spdlog::to_hex(v4, 6)); //0000: 090A0B410C4B  ...A.K
													//0006: FFFF
	UVLOG_INFO("8: {:Xs}", spdlog::to_hex(v4, 6)); //十六进制大写不间隔并且每行6个字节输出:0000: 090A0B410C4B
																						//  0006: FFFF
	std::vector<unsigned char> v5{ 9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff };
	UVLOG_INFO("9: {:Xs}", spdlog::to_hex(v5, 8)); //十六进制大写不间隔输出:0000: 090A0B410C4BFFFF
	UVLOG_INFO("10: {:Xsna}", spdlog::to_hex(v5, 8)); //十六进制大写不间隔并且不用ascii码显示:090A0B410C4BFFFF

	UVLOG_INFO("11: {:Xp}", spdlog::to_hex(v5, 8)); //十六进制大写并且不带长度提示:09 0A 0B 41 0C 4B FF FF
}

        spdlog::to_hex也可以接受字符数组或字符指针,如:

void  printHexToLog(const char* s, int len) {
	UVLOG_INFO("21: {}", spdlog::to_hex(std::string_view(s, len)));  //建议使用
	UVLOG_INFO("22: {}", spdlog::to_hex(std::initializer_list(s, s + len))); //建议使用
	UVLOG_INFO("23: {}", spdlog::to_hex(std::span(s, len))); //建议使用
	UVLOG_INFO("24: {}", spdlog::to_hex(std::string(s, len)));
	UVLOG_INFO("25: {}", spdlog::to_hex(s, s + len)); //建议使用
}

void testLog()
{
    char ss[] = "12345678888888889";

	//[1]
	UVLOG_INFO("31: {}", spdlog::to_hex(std::span(ss, sizeof(ss)))); //建议使用
	UVLOG_INFO("32: {}", spdlog::to_hex(ss, ss + sizeof(ss))); //建议使用
	UVLOG_INFO("33: {}", spdlog::to_hex(std::begin(ss), std::end(ss))); //建议使用

	//[2]
	printHexToLog(ss, sizeof(ss));
}

输出:

2024-11-09 22:21:38.400389 <thread 24096> [info] 31: 
0000: 31 32 33 34 35 36 37 38 38 38 38 38 38 38 38 38 39 00
2024-11-09 22:21:38.400401 <thread 24096> [info] 32: 
0000: 31 32 33 34 35 36 37 38 38 38 38 38 38 38 38 38 39 00
2024-11-09 22:21:38.400411 <thread 24096> [info] 33: 
0000: 31 32 33 34 35 36 37 38 38 38 38 38 38 38 38 38 39 00
2024-11-09 22:21:38.400430 <thread 24096> [info] 21: 
0000: 31 32 33 34 35 36 37 38 38 38 38 38 38 38 38 38 39 00
2024-11-09 22:21:38.400440 <thread 24096> [info] 22: 
0000: 31 32 33 34 35 36 37 38 38 38 38 38 38 38 38 38 39 00
2024-11-09 22:21:38.400452 <thread 24096> [info] 23: 
0000: 31 32 33 34 35 36 37 38 38 38 38 38 38 38 38 38 39 00
2024-11-09 22:21:38.400470 <thread 24096> [info] 24: 
0000: 31 32 33 34 35 36 37 38 38 38 38 38 38 38 38 38 39 00
2024-11-09 22:21:38.400483 <thread 24096> [info] 25: 
0000: 31 32 33 34 35 36 37 38 38 38 38 38 38 38 38 38 39 00

3.spdlog::to_hex实现原理

        查看spdlog的源码目录,在.\spdlog\include\spdlog\fmt\bin_to_hex.h里面找到了spdlog::to_hex的实现:

        1)迭代器版本

// create dump_info from ranges
template<typename It>
inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
{
    return details::dump_info<It>(range_begin, range_end, size_per_line);
}

        这个迭代器包括了C++所有容器迭代器和原生数组指针。上面代码中的

UVLOG_INFO("32: {}", spdlog::to_hex(ss, ss + sizeof(ss))); //建议使用
UVLOG_INFO("33: {}", spdlog::to_hex(std::begin(ss), std::end(ss))); //建议使用

   都会调用此版本。

           2) 容器版本

template<typename Container>
inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container, size_t size_per_line = 32)
{
    static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
    using Iter = typename Container::const_iterator;
    return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
}

当to_hex传入的容器(其中包括C++所有容器)并且容器中真实数据类型长度只能为1:

static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");

最后还是调用迭代器版本完成日志输出。

        3)std::span版本

        在C++20开始,C++标准支持了std::span,它就可以直接支持原生字符数组或字符指针加长度的形式:

#if __cpp_lib_span >= 202002L

template<typename Value, size_t Extent>
inline details::dump_info<typename std::span<Value, Extent>::iterator> to_hex(
    const std::span<Value, Extent> &container, size_t size_per_line = 32)
{
    using Container = std::span<Value, Extent>;
    static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
    using Iter = typename Container::iterator;
    return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
}

#endif

最后还是调用迭代器版本完成日志输出。      

        不管什么版本,to_hex函数都是返回dump_info:

template<typename It>
class dump_info
{
public:
    dump_info(It range_begin, It range_end, size_t size_per_line)
        : begin_(range_begin)
        , end_(range_end)
        , size_per_line_(size_per_line)
    {}

    // do not use begin() and end() to avoid collision with fmt/ranges
    It get_begin() const
    {
        return begin_;
    }
    It get_end() const
    {
        return end_;
    }
    size_t size_per_line() const
    {
        return size_per_line_;
    }

private:
    It begin_, end_;
    size_t size_per_line_;
};

于是写了特化dump_info版的formatter:

template<typename T>
struct formatter<spdlog::details::dump_info<T>, char>
{
    const char delimiter = ' ';
    bool put_newlines = true;
    bool put_delimiters = true;
    bool use_uppercase = false;
    bool put_positions = true; // position on start of each line
    bool show_ascii = false;

    // parse the format string flags
    template<typename ParseContext>
    SPDLOG_CONSTEXPR_FUNC auto parse(ParseContext &ctx) -> decltype(ctx.begin())
    {
        auto it = ctx.begin();
        while (it != ctx.end() && *it != '}')
        {
            switch (*it)
            {
            case 'X':
                use_uppercase = true;
                break;
            case 's':
                put_delimiters = false;
                break;
            case 'p':
                put_positions = false;
                break;
            case 'n':
                put_newlines = false;
                show_ascii = false;
                break;
            case 'a':
                if (put_newlines)
                {
                    show_ascii = true;
                }
                break;
            }

            ++it;
        }
        return it;
    }

    // format the given bytes range as hex
    template<typename FormatContext, typename Container>
    auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
    {
        SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
        SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
        const char *hex_chars = use_uppercase ? hex_upper : hex_lower;

#if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION < 60000
        auto inserter = ctx.begin();
#else
        auto inserter = ctx.out();
#endif

        int size_per_line = static_cast<int>(the_range.size_per_line());
        auto start_of_line = the_range.get_begin();
        for (auto i = the_range.get_begin(); i != the_range.get_end(); i++)
        {
            auto ch = static_cast<unsigned char>(*i);

            if (put_newlines && (i == the_range.get_begin() || i - start_of_line >= size_per_line))
            {
                if (show_ascii && i != the_range.get_begin())
                {
                    *inserter++ = delimiter;
                    *inserter++ = delimiter;
                    for (auto j = start_of_line; j < i; j++)
                    {
                        auto pc = static_cast<unsigned char>(*j);
                        *inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
                    }
                }

                put_newline(inserter, static_cast<size_t>(i - the_range.get_begin()));

                // put first byte without delimiter in front of it
                *inserter++ = hex_chars[(ch >> 4) & 0x0f];
                *inserter++ = hex_chars[ch & 0x0f];
                start_of_line = i;
                continue;
            }

            if (put_delimiters)
            {
                *inserter++ = delimiter;
            }

            *inserter++ = hex_chars[(ch >> 4) & 0x0f];
            *inserter++ = hex_chars[ch & 0x0f];
        }
        if (show_ascii) // add ascii to last line
        {
            if (the_range.get_end() - the_range.get_begin() > size_per_line)
            {
                auto blank_num = size_per_line - (the_range.get_end() - start_of_line);
                while (blank_num-- > 0)
                {
                    *inserter++ = delimiter;
                    *inserter++ = delimiter;
                    if (put_delimiters)
                    {
                        *inserter++ = delimiter;
                    }
                }
            }
            *inserter++ = delimiter;
            *inserter++ = delimiter;
            for (auto j = start_of_line; j != the_range.get_end(); j++)
            {
                auto pc = static_cast<unsigned char>(*j);
                *inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
            }
        }
        return inserter;
    }

    // put newline(and position header)
    template<typename It>
    void put_newline(It inserter, std::size_t pos)
    {
#ifdef _WIN32
        *inserter++ = '\r';
#endif
        *inserter++ = '\n';

        if (put_positions)
        {
            spdlog::fmt_lib::format_to(inserter, SPDLOG_FMT_STRING("{:04X}: "), pos);
        }
    }
};

       在上面代码中也可以看到前面讲到的格式符号{:X}、{:a}、{:n}、{:p}、{:s}的实现,代码不是很复杂,在这里就不一一赘述了。

4.总结

        在众多的日志输出库中,我感觉使用起来spdlog::to_hex输出十六进制是最好用的,这跟spdlog高效易用是相关的。强烈推荐大家使用。

标签:std,十六进制,38,hex,spdlog,line,size
From: https://blog.csdn.net/haokan123456789/article/details/143651558

相关文章

  • 用nginx来实现搭建Hexo个人博客
    一、配置基础环境1.1关闭防火墙systemctlstopfirewalldsetenforce02.2配置阿里云yum源mkdirshell#创建shell目录cdshell#进入目录vialiyun.sh#创建名字为aliyun的文件名的shell脚本cataliyun.sh#查看,将以下内容填入#!/bin/bashr......
  • QT中TextEdit或者QLineEdit以十六进制显示数组数据
    QT中TextEdit以十六进制显示数组数据在Qt(一种跨平台的C++图形用户界面应用程序开发框架)中,如果你想在QTextEdit中以十六进制格式显示数组数据,你可以使用以下步骤:创建一个QTextEdit控件在你的Qt应用程序中创建一个QTextEdit控件,用于显示文本。QTextEdit*textEdit=......
  • hexo 推送需要github的用户名
    问题描述hexo博客许久未写,今天尝试更新,发现hexod之后需要输入github的用户名和密码,但是我输入之后没有效果,还是无法推送到github上。研究之后发现rootcause是:github已经无法再用密码登陆推送,只能用token,而这里的token并不是ssh-keys里面的key,而是个人token。Sett......
  • 博客搭建之路:hexo博客显示阅读时间和字数
    hexo博客显示阅读时间和字数hexo版本5.0.2npm版本6.14.7next版本7.8.0效果如下在博客目录下安装npminstallhexo-symbols-count-time--save在_config.yml中加入配置symbols_count_time:#文章内是否显示symbols:truetime:true#网页底部是否显示total......
  • Hexo安装使用手册
    Hexo搭建教程知乎教程夜星梦尘CSDN个性化搭建Hexo文档Hexo中文文档STUM文档Formatter介绍属性名描述是否必需默认值title页面标题是-date页面创建日期是-type标签、分类和友情链接三个页面需要配置是-updated页面更新日期否-description页面描述否-keywords页面关......
  • 2:ARM 汇编语言2:二进制/十进制/十六进制
    2.1对于几大进制的介绍2.1.1从十进制开始(decimalsystem)十进制是啥,看起来感觉很生僻,一看就是一个高大上的词语,但是这个的含义就是我们常用的数字的规则,简洁的说就是十进一的含义,10(个位上是十,然后往十位进一,所以就是10),所以十位就是10**1 10=1*10**1+0*10**0,答案就是10......
  • [初识C语言]初识十进制、八进制以及十六进制之间的转换
     序言:本文面对的对象是C语言的初学者,我将会以最简单的方式来让大家快速了解十进制、八进制以及十六进制之间的转换。十进制的转换:十进制转换为八进制:首先我们学习:%o是printf函数中用于输出一个整数的八进制表示的格式说明符下面以十进制的整数10转换为八进制的整......
  • 博客搭建之路:hexo搜索引擎收录
    hexo搜索引擎收录hexo版本5.0.2npm版本6.14.7next版本7.8.0写博客的目的肯定不是就只有自己能看到,想让更多的人看到就需要可以让搜索引擎来收录对应的文章。hexo支持生成站点地图sitemap在hexo下的_config.yml中配置站点地图url:https://zhhll.icusitemap:url:htt......
  • 配合hexo搭建个人博客
    1,修改为淘宝yumnpmconfigsetregistryhttps://registry.npmmirror.com2,查看是否换成功npmconfiggetregistry3,安装hexo框架anpminstallhexo-cli-g4,查看hexo框架安装是否成功,成功会回显一长串版本号hexo-v5,拉取hexo,多次尝试,第一次可能拉去不成功。或者,使用管理......
  • Qt编程技巧小知识点(3)十进制与十六进制数据转化
    文章目录Qt编程技巧小知识点(3)十进制与十六进制数据转化小结Qt编程技巧小知识点(3)十进制与十六进制数据转化  上下位机之间数据交互常需要将上位机的十进制发送到下位机,这时候常常涉及到数据进制的转化,这里以十进制与十六进制的数据转化为例,来简要阐述一下:首先,我们......