fmt 是一个开源、轻量、高性能的格式化库,实现了 C++20的std::format标准 ,用来替代C中stdio和C++的iostreams。fmt的官网是 https://fmt.dev,Github代码库链接为:https://github.com/fmtlib/fmt。
本文简要介绍fmt的用法,以及对格式化语法做一些说明。
fmt用法示例
相对于 (s)printf
、cout/cerr
、stringstream
等,fmt
使用上非常简单,直接一个大括号就能格式化/输出整数、浮点数、布尔、指针、容器等绝大多数内置类型数据:
#include <fmt/core.h> #include <fmt/ranges.h> #include <iostream> #include <vector> int main() { // 字符串 auto name = fmt::format("Hello, {}\n", "tlanyan"); std::cout << name; // 输出 Hello, tlanyan // 或者直接使用fmt::print fmt::print("Hello, {}\n", "tlanyan"); // 整数 fmt::print("The answer is {}\n", 42); // 输出 The answer is 42 // 浮点数 fmt::print("PI is {:.2f}\n", 3.14159); // 输出 PI is 3.14 // 布尔 fmt::print("Are you human being? {}\n", true); // 输出Are you human being? true // vector // 需要 #include <fmt/ranges.h> fmt::print("vec: {}\n", std::vector{1, 4, 7}); // 输出 vec: [1, 4, 7] return 0; }
fmt::format
和 fmt::print
是fmt
库中最常用到的两个接口。和可以看到,只需简单的引入fmt
,就可以像 Python 一样方便的进行字符串格式化或者输出,并且格式和可读性都满足要求。
除了上面简单的进行格式化或者输出,fmt
还可以输出到文件:
// 输出到文件需要包含该头文件 #include <fmt/os.h> int main() { auto fout = fmt::output_file("readme.txt"); fout.print("{} is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams.\n", "fmt"); return 0; }
以及支持颜色和字体风格输出:
// 彩色输出需要包含该头文件 #include <fmt/color.h> int main() { fmt::print("info\n"); fmt::print(fg(fmt::color::green), "success\n"); fmt::print(fg(fmt::color::orange) | fmt::emphasis::underline, "warning\n"); fmt::print(fg(fmt::color::red) | fmt::emphasis::bold | fmt::emphasis::underline, "danger\n");; return 0; }
效果如下:
fmt格式化语法
默认情况下,fmt
已经足够使用。如果有更高级的需求,则应该了解fmt
的格式化语法。
fmt::format
和 fmt::print
函数中字符串模版的 {}
是格式化占位符,其完整形式是:
{参数id:格式化选项}
其中参数id和格式化选项都是可选的,两者可同时(不)出现,也可只出现一个。
参数id
参数id一般是从0开始的整数,不写的情况下fmt会按照出现的顺序替换成{0}、{1}等等。也可手动指定该位置使用的参数:
// 不指定参数id fmt::print("{} + {} = {}\n", 1, 1, 2); // 输出 1 + 1 = 2 // 指定参数id fmt::print("{0} + {0} = {1}\n", 1, 2); // 输出 1 + 1 = 2 fmt::print("{1} is bigger than {0}\n", 12, 25); // 输出 25 is bigger than 12
除了使用整数,fmt
也支持使用更语义化的命名字符串/别名作为参数id:
fmt::print("{first} + {second} = {sum}\n", fmt::arg("first", 1), fmt::arg("second", 2), fmt::arg("sum", 3)); // 或者使用C++11中的字面量 #include <fmt/format.h> using namespace fmt::literals; fmt::print("{first} + {second} = {sum}\n", "first"_a=1, "second"_a=2, "sum"_a=3);
其中命名字符串的命名规则同变量,以字母或者下划线开始,可以包含数字。
格式化选项
相对于参数id,格式化选项比较丰富,因为不同数据类型的格式化选项都不一样,糅合了printf
函数中的格式化以及<iomanip>中的宽度、对齐等。其完整形式为:
填充 对齐 符号 # 0 宽度 精度 类型
最常用的是 对齐、宽度、精度和类型的组合,取值范围:
- 对齐:
<
靠左对齐,>
靠右对齐,^
居中对齐 - 宽度:整数,例如20表示占据20个字符
- 精度:以
.
开始的数字,用于浮点数,例如 .6 展示小数点后6位 - 类型:基本上同printf中的类型,d表示整数、f/F/g/G/e/E表示浮点数,s表示字符串,p表示指针,b/B/o/x/X用二/八/十六进制表示整数
一个示例:
// id的值占据10位,靠右对齐,是整数 // value的值占据10位,靠右对齐,保留小数点后4位,是浮点数 fmt::print("id:{:<10d}value{:>10.4f}\n", 1, 3.14159267);
除了这些基本类型,fmt对时间格式有单独支持,具体请参考官方文档,这里仅给出一个示例:
// 需包含该头文件 // #include <fmt/chrono.h> std::time_t t = std::time(nullptr); fmt::print("{:%Y-%m-%d %H:%M:%S}\n", fmt::localtime(t)); // 输出 2023-10-08 09:05:36
fmt
对支持的每个数据类型都内置了格式化选项,一般来说无需设置格式化选项。
总结
非常推荐在项目中使用,让格式化相关代码可读性大大提升。