Skip to content

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<< 更灵活。

系统学习 C++ 生态,深入底层架构