{fmt}开源格式化库:快速安全,性能卓越,众多知名项目都在用!
{fmt} 是一个开源的格式化库,为 C 语言的 stdio 和 C++ 的 iostreams 提供了一种快速且安全的替代方案。
文档与问答
可在 StackOverflow 上使用标签 `fmt` 提问,获取文档速查表;还能在 Compiler Explorer 中尝试使用 {fmt}。
特性
{fmt} 具有众多出色特性:
- 拥有简单的格式化 API,支持使用位置参数进行本地化。
- 实现了 C++20 的 `std::format` 和 C++23 的 `std::print`,是 C++ 标准实现。
- 格式字符串语法与 Python 的 `format` 类似,有类似 Python 的格式化字符串语法。
- 运用 Dragonbox 算法,是快速的 IEEE 754 浮点数格式化器,保证正确的舍入、简洁性和往返性。
- 提供跨平台的 Unicode 支持,具备可移植的 Unicode 特性。
- 是安全的 printf 实现,包括对位置参数的 POSIX 扩展。
- 支持用户自定义类型,具备可扩展性。
- 高性能,比常见标准库实现的 `(s)printf`、iostreams、`to_string` 和 `to_chars` 更快,详情可查看速度测试和“每秒将一亿个整数转换为字符串”的测试。
- 代码体积小,在最小配置下,源代码仅由三个文件(`base.h`、`format.h` 和 `format-inl.h`)组成,编译后的代码体积也很小,具体可查看编译时间和代码膨胀测试。
- 可靠性高,拥有大量测试用例,并持续进行模糊测试。
- 安全性强,完全类型安全,格式字符串中的错误可在编译时报告,自动内存管理可防止缓冲区溢出错误。
- 易于使用,代码库小巧且自包含,无外部依赖,采用宽松的 MIT 许可证。
- 跨平台兼容性好,在不同平台上输出一致,支持旧版本编译器。
- 代码干净无警告,即使在 `-Wall -Wextra -pedantic` 等高警告级别下,代码库也不会产生警告。
- 默认与区域设置无关,无需考虑区域设置的影响。
- 可通过 `FMT_HEADER_ONLY` 宏启用可选的仅头文件配置。
更多详情请查看文档。
示例
输出到标准输出
#include <fmt/core.h>int main() { fmt::print("Hello, world!\n");}格式化字符串
std::string s = fmt::format("The answer is {}.", 42); // s == "The answer is 42."使用位置参数格式化字符串
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); // s == "I'd rather be happy than right."输出日期和时间
#include <fmt/chrono.h>int main() { auto now = std::chrono::system_clock::now(); fmt::print("Date and time: {}\n", now); fmt::print("Time: {:%H:%M}\n", now);}输出结果:
Date and time: 2023-12-26 19:10:31.557195597Time: 19:10输出容器
#include <fmt/ranges.h>#include <vector>int main() { std::vector<int> v = {1, 2, 3}; fmt::print("{}\n", v);}输出结果:
[1, 2, 3]在编译时检查格式字符串
std::string s = fmt::format("{:d}", "I am not a number");}在 C++20 中,这会导致编译时错误,因为 `d` 是字符串的无效格式说明符。
单线程写入文件
#include <fmt/os.h>int main() { auto out = fmt::output_file("guide.txt"); out.print("Don't {}", "Panic");}这比 `fprintf` 快达 9 倍。
带颜色和文本样式输出
#include <fmt/color.h>int main() { fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "Hello, {}!\n", "world"); fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | fmt::emphasis::underline, "Olá, {}!\n", "Mundo"); fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, "你好{}!\n", "世界");}在支持 Unicode 的现代终端上的输出效果更佳。
基准测试
速度测试
| 库 | 方法 | 运行时间 (s) |
|---|---|---|
| libc | printf | 0.66 |
| libc++ | std::ostream | 1.63 |
| {fmt} | fmt::print | 0.44 |
| Boost Format | boost::format | 3.89 |
| Folly Format | folly::format | 1.28 |
{fmt} 是基准测试方法中最快的,比 `printf` 快约 50%。上述结果是在 macOS 15.6.1 上使用 `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT` 编译 `tinyformat_test.cpp` 并取三次运行的最佳结果得出的。在测试中,格式字符串 `"%0.10f:%04d:%+g:%s:%p:%c:%%\n"` 或等效字符串被填充 200 万次,输出结果发送到 `/dev/null`,更多详情可参考源代码。
在 IEEE754 浮点数和双精度数格式化方面(`dtoa-benchmark`),{fmt} 比 `std::ostringstream` 和 `sprintf` 快 20 - 30 倍,也比 `double-conversion` 和 `ryu` 更快。
编译时间和代码膨胀
`format-benchmark` 中的 `bloat-test.py` 脚本用于测试非平凡项目的编译时间和代码膨胀情况。它生成 100 个翻译单元,并在每个单元中使用 `printf()` 或其替代方案 5 次,以模拟一个中等规模的项目。以下是在 Apple clang version 15.0.0 (clang - 1500.1.0.2.5)、macOS Sonoma 系统上三次运行的最佳结果:
优化构建 (-O3)
| 方法 | 编译时间 (s) | 可执行文件大小 (KiB) | 剥离后大小 (KiB) |
|---|---|---|---|
| printf | 1.6 | 54 | 50 |
| IOStreams | 28.4 | 98 | 84 |
| {fmt} | 5.0 | 54 | 50 |
| tinyformat | 32.6 | 164 | 136 |
| Boost Format | 55.0 | 530 | 317 |
{fmt} 编译速度快,在每次调用的二进制大小方面与 `printf` 相当(在该系统上误差在舍入范围内)。
非优化构建
| 方法 | 编译时间 (s) | 可执行文件大小 (KiB) | 剥离后大小 (KiB) |
|---|---|---|---|
| printf | 1.4 | 54 | 50 |
| IOStreams | 27.0 | 88 | 68 |
| {fmt} | 4.7 | 87 | 84 |
| tinyformat | 28.1 | 185 | 145 |
| Boost Format | 38.9 | 678 | 381 |
为了仅比较格式化函数的开销,`libc`、`lib(std)c++` 和 `libfmt` 均作为共享库链接。Boost Format 是仅头文件的库,因此不提供链接选项。
运行测试
若要运行测试,需参考“构建库”部分,了解如何构建库并运行单元测试。基准测试位于单独的仓库 `format-benchmark` 中,要运行基准测试,首先需要克隆该仓库并使用 CMake 生成 Makefile:
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git$ cd format-benchmark$ cmake .然后可以运行速度测试:
$ make speed-test或者运行代码膨胀测试:
$ make bloat-test代码迁移
clang - tidy v18 提供了 `modernize-use-std-print` 检查,若进行相应配置,它能够将 `printf` 和 `fprintf` 的调用转换为 `fmt::print`(默认转换为 `std::print`)。
使用该库的知名项目
众多知名项目都在使用 {fmt} 库:
- 0 A.D.:一款免费、开源、跨平台的实时策略游戏。
- AMPL/MP:一个用于数学编程的开源库。
- Apple's FoundationDB:一个开源的分布式事务性键值存储。
- Aseprite:动画精灵编辑器和像素艺术工具。
- AvioBook:一套全面的飞机运营套件。
- Blizzard Battle.net:一个在线游戏平台。
- Celestia:太空实时 3D 可视化工具。
- Ceph:一个可扩展的分布式存储系统。
- ccache:一个编译器缓存。
- ClickHouse:一个分析型数据库管理系统。
- ContextVision:医学成像软件。
- Contour:一个现代终端模拟器。
- CUAUV:康奈尔大学的自主水下航行器。
- Drake:一个用于非线性动力系统的规划、控制和分析工具箱(麻省理工学院)。
- Envoy:C++ 的 L7 代理和通信总线(Lyft)。
- FiveM:GTA V 的修改框架。
- fmtlog:一个高性能的 fmtlib 风格的日志库,延迟可达纳秒级。
- Folly:Facebook 的开源库。
- GemRB:Bioware 无限引擎的便携式开源实现。
- Grand Mountain Adventure:一款精美的开放世界滑雪和单板滑雪游戏。
- HarpyWar/pvpgn:经过优化的玩家对战游戏网络。
- KBEngine:一个开源的大型多人在线游戏(MMOG)服务器引擎。
- Keypirinha:Windows 的语义启动器。
- Kodi(原 xbmc):家庭影院软件。
- Knuth:高性能的比特币全节点。
- libunicode:一个现代的 C++17 Unicode 库。
- MariaDB:关系型数据库管理系统。
- Microsoft Verona:用于并发所有权的研究性编程语言。
- MongoDB:分布式文档数据库。
- MongoDB Smasher:一个用于生成随机数据集的小工具。
- OpenSpace:一个开源的天文可视化框架。
- PenUltima Online (POL):一个与大多数《网络创世纪》客户端兼容的 MMO 服务器。
- PyTorch:一个开源的机器学习库。
- quasardb:一个分布式、高性能的关联数据库。
- Quill:异步低延迟日志库。
- QKW:将别名泛化以简化导航,并执行复杂的多行终端命令序列。
- redis - cerberus:一个 Redis 集群代理。
- redpanda:一个比 Kafka® 快 10 倍的关键任务系统替代方案,用 C++ 编写。
- rpclib:一个现代的 C++ msgpack - RPC 服务器和客户端库。
- Salesforce Analytics Cloud:商业智能软件。
- Scylla:一个与 Cassandra 兼容的 NoSQL 数据存储,单服务器每秒可处理 100 万笔交易。
- Seastar:一个先进的开源 C++ 框架,用于现代硬件上的高性能服务器应用程序。
- spdlog:超快速的 C++ 日志库。
- Stellar:金融平台。
- Touch Surgery:手术模拟器。
- TrinityCore:开源的大型多人在线角色扮演游戏(MMORPG)框架。
- 🐙 userver framework:开源的异步框架,拥有丰富的抽象和数据库驱动。
- Windows Terminal:全新的 Windows 终端。
如果您知道其他使用该库的项目,请通过电子邮件或提交问题告知。
维护者
{fmt} 库由 Victor Zverovich(vitaut)维护,同时也有许多其他人员做出了贡献。部分贡献者和版本信息可查看“贡献者”和“版本发布”部分。如果您的贡献未被列出或提及有误,请告知,我们会及时更正。