c++ {fmt}库使用指南一

一、缘起

夜已深,时针悄悄来到了十一点半,但我的思绪却像一股涌泉般汹涌澎湃,完全无法入眠。此时,我渴望做一些有意义的事情,决定借助这份宁静,沉下心来,撰写一份关于fmt库的使用指南。
fmt库是一个高效、易用的C++格式化库,可以帮助我们方便地进行字符串格式化、输出、日志记录等操作。在这份指南中,我将介绍fmt库的基本用法、格式化字符串语法、异常处理机制等方面的内容,希望能为大家提供帮助。让我们一起开始吧!

二、基本使用

安装fmt库

github: https://github.com/fmtlib/fmt
api指南:https://fmt.dev/latest/api.html

项目中引入fmt库
  1. 老方法 cmake (不推荐)
    mkdir build
    cd build
    cmake …
    make 或者使用ide打开生成的项目文件生成相应的库静态库\
  2. 仅仅包含头文件,(需要在包含头文件之前#define FMT_HEADER_ONLY 推荐)
    • fmt/core.h:char/UTF-8主要的格式化函数,支持C++20编译时检查,依赖最小化。
    • fmt/format.h:完整的格式化API,除了额外的格式化函数之外,支持本地化(多语言支持)。
    • fmt/ranges.h:格式化ranges 和 tuples
    • fmt/chrono.h:日期和时间的格式化。
    • fmt/std.h:c++标准库类型的格式化支持。
    • fmt/compile.h:格式化字符串的编译 (编译时格式化字符串检测)。FMT_STRING(s)
    • fmt/color.h:终端颜色和文本样式。
    • fmt/os.h:提供系统API。
    • fmt/ostream.h:支持std::ostream。
    • fmt/printf.h:支持printf格式化。
    • fmt/xchar.h:可选的wchar_t支持。

Hello World

    #define FMT_HEADER_ONLY 
    #include "fmt/core.h"
    #include <string>
    int main()
    {
    
    
        // {} 占位符,可以占位int float double ....
        std::string world = fmt::format("Hello {}", "World");
        fmt::print("{}", world);
    }

三、 字符格式化语法

格式化函数如 fmt::format() 和 fmt::print() 都使用相同的语法,它由 {} 包围的“替换字段”。
未包含在花括号中的任何内容都被视为文字文本,将不加修改地复制到输出中。果需要在字面文本中包含一个花括号字符,可以通过重复使用花括号来转义:{ { 和 }}。
在大多数情况下,语法与 printf 格式相似,但添加了 {} 并使用 : 代替 %。例如,“%03.2f” 可以转换为 “{:03.2f}”,只是把%替换了{} 但是fmt功能更强大
输出格式如下:
replacement_field ::= “{” [arg_id] [“:” (format_spec | chrono_format_spec)] “}” // 替换字符= {[参数id]:[format_spec] | [chrono_format_spec]}
arg_id ::= integer | identifier // 整数0-9 a-z A-Z, 下面的是表示哪个代表什么意思
integer ::= digit+
digit ::= “0”…“9”
identifier ::= id_start id_continue*
id_start ::= “a”…“z” | “A”…“Z” | “_”
id_continue ::= id_start | digit\

替换格式以 “:” 为分界符,“:” 前面的的代表参数id, 就是指参数的顺序,可以是1、2、3、4、… 也可以是a、b 、c … “:” 后面的就是专门针对数字型(整形、浮动点型) 时间类型定义的格式。[] 代表是可选的, | 代表或,只能存在一种

3.1参数id 在":" 之前

    // 只有参数id时候,: 可以省略不写
    fmt::print("name:{1}, age: {0:}", 42, "knox");

3.2format_spec 在":" 之后 (Format Specification Mini-Language 格式化规范迷你语言)

  1. 组成
    format_spec ::= [[fill]align][sign][“#”][“0”][width][“.” precision][“L”][type]
    fill ::= <a character other than ‘{’ or ‘}’>
    align ::= “<” | “>” | “^”
    sign ::= “+” | “-” | " "
    width ::= integer | “{” [arg_id] “}”
    precision ::= integer | “{” [arg_id] “}”
    type ::= “a” | “A” | “b” | “B” | “c” | “d” | “e” | “E” | “f” | “F” | “g” | “G” |
    “o” | “p” | “s” | “x” | “X”\

  2. file 代表填充字符,填充字符可以是除了“{”和“}”之外的任何 Unicode 代码点。填充字符的存在是由其后面的字符表示的,该字符必须是对齐选项之一。如果 format_spec 的第二个字符不是有效的对齐选项(<、>、^),则假定填充字符和对齐选项都不存在。

  3. align 代表对其方式 ,< 代表左对齐, >代表右对齐, ^代表居中

  4. sign 只针对数字,“+”表示正数和负数都要使用符号。“-”表示只有负数需要使用符号(这是默认行为)。空格表示正数前面要加一个空格,负数前面要加一个减号。

  5. 此选项仅适用于整数和浮点数类型。对于整数,当使用二进制、八进制或十六进制输出时,此选项会将相应的前缀“0b”(“0B”)、“0”或“0x”(“0X”)添加到输出值中, 小写字符格式出来的是小写,如,{:#0X}, 255 = 0XFF, ,{:#0x}, 255 = 0xff, 其他的进制类似,

  6. width 十进制的整数,只适用于数值型,用于定义最小字段宽度。如果未指定,则字段宽度将由内容确定。在宽度字段之前加上零(‘0’)字符可以启用数值类型的符号感知零填充。
    它强制在符号或基数(如果有)之后但在数字之前放置填充。这用于以“+000000120”形式打印字段。

  7. “.” precision 代表精度。精度只存在于 浮点型中,整数、字符、布尔和指针值,是不允许使用精度的,对于非数字类型,字段指示最大字段大小,就算指定了大小。C字符串必须以空字符结尾.
    一般精度.nf, 不是精度.n n代表需要格式化几个。

    // 非数字指定的是输出的字符的个数。
    fmt::print("name:{1}, age: {0:.5}", "1234567890", "knox");
    // 输出:name:knox, age: 12345
    
    // c字符串不够怎么办呢,最大字符串长
    fmt::print("name:{1}, age: {0:.5}", "123", "knox");
    // 输出:name:knox, age: 123
    
    // 对于数字代表精度,只对浮点型有用
    fmt::print("{:.2f}", 42.0f);
    // 输出:42.00
    //fmt::print("{:.2f}", 42); 编译报错
    //fmt::print("{:.2f}", true); 编译报错

  1. “L”选项使用当前区域设置来插入适当的数字分隔符。此选项仅适用于数值类型。
    auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
    fmt::print("{}", s);
    // 输出: 1,234,567,890
  1. type
    'e’指数表示法。指数标记使用小写字母 ‘e’。
    'E’指数表示法。指数标记使用大写字母 ‘E’。
    'f’定点表示法。将数字显示为定点数。
    'F’定点表示法。将数字显示为定点数。与 ‘f’ 相同。
    'g’通用格式。根据数字的大小和指定的精度,使用定点表示法或指数表示法。
    'G’通用格式。与 ‘g’ 相同,但使用指数标记使用大写字母 ‘E’。
    'x’十六进制整数。将数字显示为十六进制数。
    'X’十六进制整数。将数字显示为大写十六进制数。
    'a’十六进制浮点数。使用小写字母 ‘a’ 作为指数标记。
    'A’十六进制浮点数。使用大写字母 ‘A’ 作为指数标记。
    'c’字符。将整数解释为 Unicode 字符。
    's’字符串。将参数格式化为字符串。
    'p’指针。将指针格式化为十六进制数。

chrono_format_spec 时间格式化

1.组成
chrono_format_spec ::= [[fill]align][width][“.” precision][chrono_specs]
chrono_specs ::= [chrono_specs] conversion_spec | chrono_specs literal_char
conversion_spec ::= “%” [modifier] chrono_type
literal_char ::= <a character other than ‘{’, ‘}’ or ‘%’>\ // 除了{ } % 这三个字符都可以做链接符
modifier ::= “E” | “O”
chrono_type ::= “a” | “A” | “b” | “B” | “c” | “C” | “d” | “D” | “e” | “F” |
“g” | “G” | “h” | “H” | “I” | “j” | “m” | “M” | “n” | “p” |
“q” | “Q” | “r” | “R” | “S” | “t” | “T” | “u” | “U” | “V” |
“w” | “W” | “x” | “X” | “y” | “Y” | “z” | “Z” | “%”\

  1. [[fill]align] 与3.2.1 相似
  2. 天数小于10,分钟数,秒数,月数,都会在前面添加0
    关于fmt 格式化字符串更详细的语法请移步https://fmt.dev/latest/syntax.html#grammar-token-sf-identifier

格式化字符串示例

按arg_id 顺序输出,id从0开始
    // 序号从零开始
    fmt::print("{0}, {1}, {2}\n", 'a', 'b', 'c');
    // print: "a, b, c"


    fmt::print("{}, {}, {}\n", 'a', 'b', 'c');
    // print: "a, b, c"

    fmt::print("{2}, {1}, {0} \n", 'a', 'b', 'c');
    // print: "c, b, a"
    // fmt::print("{2}, {1}, {0} {3}\n", 'a', 'b', 'c'); 编译报错,没有第四个参数

    // 输出
    // a, b, c  
    // a, b, c  
    // c, b, a  
用自定的字符填充空白符,并指定居中方式
    // 不指定填充符号默认为空格, 如果不存在 <>^ 就假定填充符号都不存在
    fmt::print("{:<30}\n", "left aligned");
    // 
    fmt::print("{:<<30}\n", "left aligned");
                
    fmt::print("{:>30}\n", "right aligned");
    fmt::print("{:>>30}\n", "right aligned");

    fmt::print("{:^30}\n", "centered");
          
    fmt::print("{:^^30}\n", "centered"); 

    // 输出
    //left aligned                  
    //left aligned<<<<<<<<<<<<<<<<<<
    //                right aligned 
    //>>>>>>>>>>>>>>>>>right aligned
    //        centered              
    //^^^^^^^^^^^centered^^^^^^^^^^^

动态设置宽度和精度
    // 可以动态设置宽度和精度,但仅仅限制于此,
    // 动态设置宽度的时候,宽度arg_id 为 参数+1, 
    //           0 1   2 3  参数arg_id 可以数{ 的个数,当然{} 一定是成对出现的。
    fmt::print("{:<{}} {:.{}f} \n", "left aligned", 30, 3.14, 1);
    fmt::print("{:.{}f}\n", 3.14, 1);
    // 输出
    // left aligned                   3.1  
    // 3.1  

:+ :- 号的使用
    // + 代表正数加+号,负数加-号
    fmt::print("{:+f}; {:+f}\n", 3.14, -3.14); 
    // 空格正数加空格,负数加-号
    fmt::print("{: f}; {: f}\n", 3.14, -3.14); 
    // -号代表正数不变,负数加-号 same as '{:f}; {:f}' 相当于是默认行为
    fmt::print("{:-f}; {:-f}\n", 3.14, -3.14);

    fmt::print("{:+}; {:+}\n", 3, -3);
    fmt::print("{:-}; {:-}\n", 3, -3);
    fmt::print("{: }; {: }\n", 3, -3);
    fmt::print("{:}; {:}\n", 3, -3);

    // 输出
    //+3.140000; -3.140000
    // 3.140000; -3.140000
    //3.140000; -3.140000
    //+3; -3
    //3; -3
    // 3; -3
    //3; -3
进制输出
    // # 加上符号 0x 0 0b
    fmt::print("int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}\n", 42);
    
    fmt::print("int: {0:d};  hex: {0:#x};  oct: {0:#o};  bin: {0:#b}\n", 42);

    // #06 代表宽度为6个, 不够的在进制之前使用0填充, 超出指定大小被忽略
    fmt::print("int: {0:d};  hex: {0:#06x};  oct: {0:#06o};  bin: {0:#06b}\n", 42);
    fmt::print("int: {0:d};  hex: {0:#01x};  oct: {0:#02o};  bin: {0:#03b}\n", 42);
    // 输出
    // int: 42;  hex: 2a;  oct: 52; bin: 101010
    // int: 42;  hex: 0x2a;  oct: 052;  bin: 0b101010
    // int: 42;  hex: 0x002a;  oct: 000052;  bin: 0b101010
    // int: 42;  hex: 0x2a;  oct: 052;  bin: 0b101010
使用填充字符打印边框
    fmt::print(
        "┌{0:─^{2}}┐\n"
        "│{1: ^{2}}│\n"
        "└{0:─^{2}}┘\n", "", "Hello, knox!", 20);

有点抗不住了,今天就先到这里了,明天我们继续刚fmt库的api详细用法。

猜你喜欢

转载自blog.csdn.net/qq_33944628/article/details/129943438