C++ fmt库的使用小结

 更新时间:2026年07月05日 11:33:58   作者:千里马-horse  
本文介绍了C++ fmt库的使用指南,重点突出其简洁、安全、高效的特点,文章涵盖格式化输出、容器/时间格式化及常见问题,具有一定的参考价值,感兴趣的可以了解一下

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 实现,同时保持高性能和跨平台兼容性。

到此这篇关于C++ fmt库的使用小结的文章就介绍到这了,更多相关C++ fmt库内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • 深入理解Qt中各种消息框对话框的使用

    深入理解Qt中各种消息框对话框的使用

    本篇文章主要介绍了Qt中各种消息框的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • C++简易通讯录系统实现流程详解

    C++简易通讯录系统实现流程详解

    这篇文章主要为大家介绍了C语言简易版通讯录的具体实现流程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • C++高性能服务器框架之协程调度模块

    C++高性能服务器框架之协程调度模块

    这篇文章主要介绍了C++高性能服务器框架中的协程调度模块,文中通过代码示例介绍的非常详细,对我们的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-06-06
  • C语言预处理预编译命令及宏定义详解

    C语言预处理预编译命令及宏定义详解

    这篇文章主要为大家介绍了C语言预处理预编译命令及宏定义的详解,其中包含运行环境命名约定条件及#under等基础详解,有需要的朋友可以借鉴参考下
    2021-10-10
  • C++ List链表的介绍和使用

    C++ List链表的介绍和使用

    list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代,这篇文章主要介绍了C++ List链表的介绍和使用,需要的朋友可以参考下
    2023-03-03
  • c/c++的opencv椒盐噪声的实现

    c/c++的opencv椒盐噪声的实现

    椒盐噪声是一种简单的图像噪声模型,通过在C/C++中利用随机数生成器,我们可以有效地模拟这种噪声,本文就来介绍一下如何实现,感兴趣的可以了解一下
    2025-05-05
  • 从零实现一个 C++ 轻量级日志系统原理与实践指南

    从零实现一个 C++ 轻量级日志系统原理与实践指南

    本文从零开始手搓一个简易C++日志工具,详细介绍了日志系统的理念、设计、实现及优化,核心包括:日志格式化时间和等级,基于策略模式实现异步刷新,使用RAII机制实现自动刷新,以及对线程安全和可重入性的深度考量,最后还提供了优化方向,帮助读者完善日志系统
    2026-05-05
  • C++枚举类型用法总结(枚举字符常量代替常量)

    C++枚举类型用法总结(枚举字符常量代替常量)

    这篇文章主要介绍了C++枚举类型用法总结(枚举字符常量代替常量),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • 简单讲解C语言中宏的定义与使用

    简单讲解C语言中宏的定义与使用

    这篇文章主要介绍了C语言中宏的定义与使用,是C语言入门学习中的基础知识,需要的朋友可以参考下
    2016-05-05
  • C语言数据结构实例讲解单链表的实现

    C语言数据结构实例讲解单链表的实现

    单链表是后面要学的双链表以及循环链表的基础,要想继续深入了解数据结构以及C++,我们就要奠定好这块基石!接下来就和我一起学习吧
    2022-03-03

最新评论