首页 > 编程开发 > C类语言    日期:2026-07-05 / 浏览

fmt 库的核心优势是 用法简洁、类型安全、兼容多场景,以下是从基础到进阶的完整使用指南,涵盖常用功能和关键场景。

一、前置准备:环境配置

使用前需确保 fmt 库已集成到项目中

  1. 编译依赖:链接 fmt 库(CMake 项目直接链接 fmt::fmt 目标,非 CMake 项目需手动添加头文件和 src/format.cc 编译)。
  2. 头文件引入:核心功能只需包含主头文件:
    #include <fmt/format.h>   // 核心格式化功能(fmt::format、fmt::print 等)
    #include <fmt/ranges.h>   // 容器格式化(可选,如 vector、map)
    #include <fmt/chrono.h>   // 时间格式化(可选,如 std::chrono)
    

二、核心用法:格式化输出与字符串构建

fmt 的核心 API 是 fmt::format(构建字符串)和 fmt::print(直接输出),语法兼容 Python 格式化风格,比 printf 更简洁,比 iostreams 更高效。

1. 基础格式化(类似 printf,但更安全)

支持位置参数、类型自动推导,无需手动指定格式符(如 %d %s),错误会在编译时捕获。

#include <fmt/format.h>
#include <iostream>

int main() {
    // 1. 基础类型格式化(int、string、float 等)
    std::string msg1 = fmt::format("整数:{},字符串:{},浮点数:{:.2f}", 42, "hello", 3.1415);
    std::cout << msg1 << std::endl;  // 输出:整数:42,字符串:hello,浮点数:3.14

    // 2. 位置指定(解决参数顺序问题)
    std::string msg2 = fmt::format("第2个参数:{1},第1个参数:{0}", "A", "B");
    std::cout << msg2 << std::endl;  // 输出:第2个参数:B,第1个参数:A

    // 3. 直接输出到控制台(无需手动拼接 cout)
    fmt::print("直接输出:{} + {} = {}\n", 10, 20, 30);  // 输出:直接输出:10 + 20 = 30

    // 4. 输出到文件(支持 FILE* 或 std::ostream)
    fmt::print(stdout, "输出到标准输出\n");
    fmt::print(stderr, "输出到标准错误\n");
    return 0;
}

2. 容器格式化(开箱即用,无需手动遍历)

包含 <fmt/ranges.h> 后,支持 std::vectorstd::arraystd::map 等容器直接格式化,默认输出类似 JSON 风格。

#include <fmt/ranges.h>
#include <vector>
#include <map>

int main() {
    std::vector<int> nums = {1, 2, 3, 4};
    fmt::print("向量:{}\n", nums);  // 输出:向量:[1, 2, 3, 4]

    std::map<std::string, int> score = {{"Alice", 95}, {"Bob", 88}};
    fmt::print("字典:{}\n", score);  // 输出:字典:{"Alice": 95, "Bob": 88}

    // 自定义分隔符(通过格式说明符)
    fmt::print("向量(空格分隔):{:v}\n", fmt::join(nums, " "));  // 输出:1 2 3 4
    return 0;
}

3. 时间格式化(兼容 std::chrono)

包含 <fmt/chrono.h> 后,支持时间点、时间段的格式化,无需手动转换 time_t

#include <fmt/chrono.h>
#include <chrono>

int main() {
    // 格式化当前时间(本地时间)
    auto now = std::chrono::system_clock::now();
    fmt::print("当前时间:{:%Y-%m-%d %H:%M:%S}\n", now);  // 输出:当前时间:2024-10-01 14:30:00

    // 格式化时间段
    std::chrono::duration<double> diff = 2.5h + 30min;
    fmt::print("时长:{}\n", diff);  // 输出:时长:2h30m0s
    fmt::print("时长(秒):{:.1f}s\n", diff);  // 输出:时长(秒):9000.0s
    return 0;
}

三、进阶用法:自定义类型与高级特性

1. 自定义类型格式化(可扩展性核心)

通过实现 fmt::formatter 模板特化,让自定义类支持 fmt 格式化,还能兼容编译时检查。

#include <fmt/format.h>

// 自定义类
struct Point {
    int x, y;
};

// 为 Point 实现 formatter 特化
template <>
struct fmt::formatter<Point> {
    // 解析格式说明符(可选,如 {:x} 控制输出格式)
    constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
        return ctx.end();  // 此处不处理自定义格式符,直接返回结束迭代器
    }

    // 格式化逻辑
    template <typename FormatContext>
    auto format(const Point& p, FormatContext& ctx) -> decltype(ctx.out()) {
        // 拼接输出:(x, y)
        return fmt::format_to(ctx.out(), "({}, {})", p.x, p.y);
    }
};

int main() {
    Point p = {10, 20};
    fmt::print("坐标:{}\n", p);  // 输出:坐标:(10, 20)
    return 0;
}

2. 避免缓冲区溢出:安全写入

fmt::format_to_n 可限制输出长度,防止缓冲区溢出,返回实际写入的字符数和是否截断的标记。

#include <fmt/format.h>
#include <array>

int main() {
    std::array<char, 10> buf;  // 固定大小缓冲区(最多存 9 个字符 + 终止符)
    auto [size, truncated] = fmt::format_to_n(buf.data(), buf.size() - 1, "Hello {}", "World");
    buf[size] = '\0';  // 手动添加字符串终止符

    fmt::print("结果:{},是否截断:{}\n", buf.data(), truncated);  // 输出:结果:Hello W,是否截断:true
    return 0;
}

3. 动态参数(运行时确定参数个数)

当参数个数不确定时,使用 fmt::dynamic_format_arg_store 动态添加参数,适合日志、配置等场景。

#include <fmt/format.h>
#include <vector>

int main() {
    fmt::dynamic_format_arg_store<fmt::format_context> args;
    args.push_back("name");
    args.push_back("Alice");
    args.push_back("age");
    args.push_back(25);

    // 动态参数格式化
    std::string msg = fmt::vformat("{}: {}, {}: {}", args);
    fmt::print("{}\n", msg);  // 输出:name: Alice, age: 25
    return 0;
}

四、特殊场景:与 C 标准库 / STL 兼容

1. 兼容 printf 格式符

如果习惯 printf 的格式符(如 %04d %x),fmt 完全支持,同时保留类型安全。

#include <fmt/format.h>

int main() {
    // 用 printf 格式符格式化
    fmt::print("十六进制:{:x}\n", 255);    // 输出:ff
    fmt::print("补零对齐:{:04d}\n", 42);   // 输出:0042
    fmt::print("科学计数法:{:e}\n", 123.4); // 输出:1.234000e+02
    return 0;
}

2. 格式化 STL 字符串与流

支持 std::stringstd::wstring,也可输出到 std::ostream(如 std::cout)。

#include <fmt/format.h>
#include <string>
#include <iostream>

int main() {
    std::string name = "Bob";
    // 格式化 std::string
    std::string msg = fmt::format("Hello, {}", name);
    std::cout << msg << std::endl;  // 输出:Hello, Bob

    // 直接输出到 std::ostream
    fmt::print(std::cout, "Width: {}\n", 100);  // 等价于 std::cout << "Width: 100" << std::endl
    return 0;
}

五、常见问题与注意事项

  1. 编译错误:若使用容器 / 时间格式化时报错,检查是否包含对应的头文件(<fmt/ranges.h> <fmt/chrono.h>)。
  2. C++ 版本要求:核心功能支持 C++11 及以上,部分高级特性(如编译时格式检查)需 C++17+。
  3. 性能优化:频繁格式化场景可使用 fmt::memory_buffer 复用缓冲区,减少内存分配:
    fmt::memory_buffer buf;
    fmt::format_to(buf, "First: {}", 1);
    fmt::format_to(buf, ", Second: {}", 2);
    std::string result = fmt::to_string(buf);  // 结果:"First: 1, Second: 2"
    

总结

fmt 库的使用核心是 “简洁 + 安全”:基础场景用 fmt::format/fmt::print 替代 printf/iostreams,进阶场景(自定义类型、动态参数)通过扩展 formatter 或 dynamic_format_arg_store 实现,同时保持高性能和跨平台兼容性。

觉得上面的内容有用吗?快来点个赞吧!

点赞() 我要打赏

温馨提示 : 本站内容来自会员投稿以及互联网,所有源码及教程均为作者总结编辑,请大家在使用过程中提前做好备份,以免发生无法预知的错误,源码类教程请勿直接用于生产环境!

 可能感兴趣的文章