fmtlib — 格式化输出
fmtlib({fmt})是 C++ 最快的格式化库,也是 C++20
std::format的原型。类型安全、比 printf 快、比 iostream 简洁。
安装
bash
# vcpkg
vcpkg install fmt
# CMake
find_package(fmt REQUIRED)
target_link_libraries(myapp PRIVATE fmt::fmt)
# 或者 header-only
target_link_libraries(myapp PRIVATE fmt::fmt-header-only)基本格式化
cpp
#include <fmt/core.h>
#include <fmt/format.h>
// 基本用法
std::string s = fmt::format("Hello, {}!", "world");
fmt::print("Hello, {}!\n", "world"); // 直接输出
// 位置参数
fmt::format("{0} {1} {0}", "hello", "world"); // "hello world hello"
// 命名参数
fmt::format("name={name}, age={age}", fmt::arg("name", "Alice"), fmt::arg("age", 30));格式规范
cpp
// 整数
fmt::format("{:d}", 42); // "42"(十进制)
fmt::format("{:b}", 42); // "101010"(二进制)
fmt::format("{:o}", 42); // "52"(八进制)
fmt::format("{:x}", 255); // "ff"(十六进制小写)
fmt::format("{:X}", 255); // "FF"(十六进制大写)
fmt::format("{:#x}", 255); // "0xff"(带前缀)
fmt::format("{:08d}", 42); // "00000042"(补零)
fmt::format("{:+d}", 42); // "+42"(显示正号)
fmt::format("{:,}", 1000000); // "1,000,000"(千位分隔符,C++20)
// 浮点
fmt::format("{:.2f}", 3.14159); // "3.14"
fmt::format("{:.4e}", 3.14159); // "3.1416e+00"
fmt::format("{:.3g}", 3.14159); // "3.14"(自动选择格式)
fmt::format("{:10.3f}", 3.14); // " 3.140"(宽度10,右对齐)
// 字符串对齐
fmt::format("{:>10}", "right"); // " right"(右对齐)
fmt::format("{:<10}", "left"); // "left "(左对齐)
fmt::format("{:^10}", "center"); // " center "(居中)
fmt::format("{:*^10}", "hi"); // "****hi****"(填充字符)自定义类型格式化
cpp
#include <fmt/format.h>
struct Point { double x, y; };
// 特化 fmt::formatter
template<>
struct fmt::formatter<Point> {
// 解析格式规范(可选)
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
// 格式化
auto format(const Point& p, format_context& ctx) const {
return fmt::format_to(ctx.out(), "({:.2f}, {:.2f})", p.x, p.y);
}
};
Point p{1.5, 2.7};
fmt::print("Point: {}\n", p); // "Point: (1.50, 2.70)"
// 支持格式规范的自定义类型
template<>
struct fmt::formatter<Point> : fmt::formatter<std::string> {
auto format(const Point& p, format_context& ctx) const {
return fmt::formatter<std::string>::format(
fmt::format("({}, {})", p.x, p.y), ctx);
}
};输出到各种目标
cpp
#include <fmt/ostream.h>
#include <fmt/color.h>
#include <fmt/chrono.h>
#include <fmt/ranges.h>
// 输出到文件
FILE* f = fopen("out.txt", "w");
fmt::print(f, "Hello, {}!\n", "file");
// 输出到字符串(高效)
std::string buf;
fmt::format_to(std::back_inserter(buf), "Hello, {}!", "world");
// 固定大小缓冲区
char arr[100];
auto result = fmt::format_to_n(arr, sizeof(arr), "Hello, {}!", "world");
// result.size = 实际写入字节数
// 彩色输出
fmt::print(fmt::fg(fmt::color::red), "红色文字\n");
fmt::print(fmt::fg(fmt::color::green) | fmt::emphasis::bold, "绿色加粗\n");
fmt::print(fmt::bg(fmt::color::blue) | fmt::fg(fmt::color::white), "蓝底白字\n");
// 时间格式化
#include <chrono>
auto now = std::chrono::system_clock::now();
fmt::print("{:%Y-%m-%d %H:%M:%S}\n", now);
// 容器格式化
std::vector<int> v = {1, 2, 3, 4, 5};
fmt::print("{}\n", v); // [1, 2, 3, 4, 5]
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
fmt::print("{}\n", m); // {"a": 1, "b": 2}性能
cpp
// fmtlib 比 printf 快,比 iostream 快很多
// 基准测试(格式化 100 万次整数):
// printf: ~150ms
// iostream: ~400ms
// fmt: ~80ms
// std::format: ~90ms(C++20,基于 fmtlib)
// 编译期格式字符串检查
fmt::format("{:d}", "not an int"); // 编译错误!类型不匹配
// 运行时格式字符串(动态)
std::string fmt_str = "Hello, {}!";
fmt::vformat(fmt_str, fmt::make_format_args("world"));与 std::format 的关系
cpp
// C++20 std::format 基于 fmtlib,API 几乎相同
#include <format> // C++20
std::string s = std::format("Hello, {}!", "world");
std::print("Hello, {}!\n", "world"); // C++23
// fmtlib 的优势:
// - 支持更多特性(颜色、容器格式化等)
// - 在 C++17 项目中可用
// - 通常比标准库实现更快关键认知
fmtlib 是现代 C++ 格式化的标准选择。C++20 项目直接用 std::format,C++17 项目用 fmtlib。自定义类型只需特化 fmt::formatter,比重载 operator<< 更灵活。