一、缘起
夜已深,时针悄悄来到了十一点半,但我的思绪却像一股涌泉般汹涌澎湃,完全无法入眠。此时,我渴望做一些有意义的事情,决定借助这份宁静,沉下心来,撰写一份关于fmt库的使用指南。
fmt库是一个高效、易用的C++格式化库,可以帮助我们方便地进行字符串格式化、输出、日志记录等操作。在这份指南中,我将介绍fmt库的基本用法、格式化字符串语法、异常处理机制等方面的内容,希望能为大家提供帮助。让我们一起开始吧!
二、基本使用
安装fmt库
github: https://github.com/fmtlib/fmt
api指南:https://fmt.dev/latest/api.html
项目中引入fmt库
- 老方法 cmake (不推荐)
mkdir build
cd build
cmake …
make 或者使用ide打开生成的项目文件生成相应的库静态库\ - 仅仅包含头文件,(需要在包含头文件之前#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 格式化规范迷你语言)
-
组成
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”\ -
file 代表填充字符,填充字符可以是除了“{”和“}”之外的任何 Unicode 代码点。填充字符的存在是由其后面的字符表示的,该字符必须是对齐选项之一。如果 format_spec 的第二个字符不是有效的对齐选项(<、>、^),则假定填充字符和对齐选项都不存在。
-
align 代表对其方式 ,< 代表左对齐, >代表右对齐, ^代表居中
-
sign 只针对数字,“+”表示正数和负数都要使用符号。“-”表示只有负数需要使用符号(这是默认行为)。空格表示正数前面要加一个空格,负数前面要加一个减号。
-
此选项仅适用于整数和浮点数类型。对于整数,当使用二进制、八进制或十六进制输出时,此选项会将相应的前缀“0b”(“0B”)、“0”或“0x”(“0X”)添加到输出值中, 小写字符格式出来的是小写,如,{:#0X}, 255 = 0XFF, ,{:#0x}, 255 = 0xff, 其他的进制类似,
-
width 十进制的整数,只适用于数值型,用于定义最小字段宽度。如果未指定,则字段宽度将由内容确定。在宽度字段之前加上零(‘0’)字符可以启用数值类型的符号感知零填充。
它强制在符号或基数(如果有)之后但在数字之前放置填充。这用于以“+000000120”形式打印字段。 -
“.” 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); 编译报错
- “L”选项使用当前区域设置来插入适当的数字分隔符。此选项仅适用于数值类型。
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
fmt::print("{}", s);
// 输出: 1,234,567,890
- 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” | “%”\文章来源:https://www.toymoban.com/news/detail-558991.html
- [[fill]align] 与3.2.1 相似
- 天数小于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详细用法。文章来源地址https://www.toymoban.com/news/detail-558991.html
到了这里,关于c++ {fmt}库使用指南一的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!