【C++20】format格式化输出

embedded/2025/4/2 7:44:16/

C++20 format格式化输出

在C++20之前,格式化能力都依赖于三方格式化库FMT, 而C++20 标准委员会终于在C++标准库引入了格式化功能,从使用方式和风格来看其实就是FMT库转正了

直接使用

包含<format.h>头文件既可以直接使用,类似python 使用{} 作为占位符,{} 会被指定的内容替换

  • 输出内置类型
    #include<format>
    std::cout << std::format("hello {}", "world") << std::endl; // 输出 hello world
    std::cout << std::format("int: {}, bool: {}, double: {},float: {}", 1, true, 1.2, 1.1f); // 输出 int: 1, bool: true, double: 1.2,float: 1.1
    // wstring 测试
    std::wcout << std::format(L"wstring test {}", L"content") << std::endl; // 输出 wstring text conent
    
  • 如果想输出占位符{} 怎么办, 只需要在外层再套一个{} 即可
    std::cout << std::format("{{}}")  << std::endl; // 输出{}
    
  • 指定顺序输出
    如果只使用{} 占位符号,输出的参数将从头到尾逐一替换{} 进行输出,如果需要指定替换顺序,只要在{} 增加序号表明替换顺序
    // 输出 this is seq test, first, second
    std::cout << std::format("this is seq test, {1}, {0}", "second", "first") << std::endl;  
    
  • 格式化输出
    在输出一些整型和浮点数时,我们会有符号输出、位数和对齐的需求,在format中可以通过下列方式实现
    • +:始终带符号输出,正数带+,负数带-
    • -: 只有负数带-
    • 空格: 在正数前面带空格,负数前面带-
    int iValue = 10;
    // 0表示第一个参数,冒号表示格式的开始
    std::format("{0:}, {0:+}, {0:-}, {0: }", iValue) << std::endl; // 输出10, +10, 10,  10
    
    • 0: 冒号后面的0表示填充字符,如果输出的字符宽度小于定义宽度,将使用0进行填充,输出值是0将忽略
    int iValue = 10;
    std::cout << std::format("{:06d}", iValue)<< std::endl; // 输出000010
    
    • #: 对参数输出的形式进行替换,比如对于不同进制的整型数据进行输出时,会在输出参数前面用0b,0x,00的形式进行替换
    // 输出 0b1010, 012, 0xa
    std::cout << std::format("{0:#b}, {0:#o}, {0:#x}", iValue) << std::endl;
    
  • 填充与对齐
    填充与对齐主要包含以下三个字符,分别是>,<,^。
    • >:强制输出,右对齐,使用指定符号填充
    • <: 强制输出,左对齐,使用指定符号填充
    • ^: 可输出中间内容
    int iValue = 10;
    std::cout << std::format("{0:0>6d}, {0:0<6d}, {0:0^6d}", iValue) << std::endl; // 输出000010, 100000, 001000
    
  • 精度与宽度
    使用.和位宽可以指定浮点数的精度和宽度
    float fValue = 3.1415
    std::cout << std::format("{0:.2f}, {0:.6f}", fValue); // 输出 3.14, 3.141500
    

自定义扩展

通过自定义扩展可以让std::format方法格式化输出自定义类型

  • 对std::formatter模板进行模板特化实现,参考cppreference demo的例子
  • 特化之后我们需要实现两个模板函数
    • template<class ParseContext> constexpr ParseContext::iterator parse(ParseContext& ctx) 输出格式解析参数,可以自定义格式,比如'{:#}'格式
    • template<class FmtContext>FmtContext::iterator format(QuotableString s, FmtContext& ctx) const 对内容进行格式化的函数
    • 例子
    struct Person {std::string name { "hhh" };int age { 18 };bool man { false };
    };// 针对person进行模板特化
    template <>
    struct std::formatter<Person> {// 对格式进行解析,这里我们没有定制constexpr auto parse(std::format_parse_context& ctx){auto it = ctx.begin();if (it == ctx.end()) {return it;}if (*it != '}') {throw format_error("invalid param");}return it;}template <class FmtContext>auto format(const Person& person, FmtContext& ctx) const{//  根据我们想要的格式进行输出return std::format_to(ctx.out(), "name is {}, age is {}, sex is {}", person.name, person.age, person.man ? "man" : "woman");}
    };
    
  • 如果不想实现parse 函数,我们也可以继承已有的std::formatter类
    • 例子
    struct Person {std::string name { "hhh" };int age { 18 };bool man { false };
    };// 继承已有的formatter类
    template <>
    struct std::formatter<Person> : std::formatter<std::string> {template <class FmtContext>auto format(const Person& person, FmtContext& ctx) const{return std::format_to(ctx.out(), "name is {}, age is {}, sex is {}", person.name, person.age, person.man ? "man" : "woman");}
    };
    
  • 如果自定义类型是模板类该怎么处理
    • 例子
    template <typename T1, typename T2, typename T3>
    struct CustomeTypeStruct {T1 pName;T2 iAge;T3 iScore;
    };
    // 特化formatter时也增加上自定义模板类的类型
    template <typename T1, typename T2, typename T3, typename CharT>
    struct std::formatter<CustomeTypeStruct<T1, T2, T3>, CharT> : std::formatter<T1, CharT> {template <class FormatContext>auto format(CustomeTypeStruct<T1, T2, T3>& stu, FormatContext& fc){return std::format_to(fc.out(), "T1:{}, T2:{}, T3:{}", stu.pName, stu.iAge, stu.iScore);}
    };

参考

https://mp.weixin.qq.com/s/Rll2rKfpj-6xPlcl5nYaYw
https://en.cppreference.com/w/cpp/utility/format/formatter


http://www.ppmy.cn/embedded/178245.html

相关文章

记录vite-plugin-dts打包时无法生成 .d.ts文件问题

项目中build 时候需要生成如下dist中的main.d.ts&#xff0c; 如果配置vite additionalData会造成预编译scss报错 preprocessorOptions: {scss: {additionalData: import "/assets/styles/variables.scss";,// 刚开始使用下面两个配置可以打包出来&#xff0c;但是…

路由器、交换机、防火墙、服务器、负载均衡在网络中作用

1. 路由器&#xff08;Router&#xff09; 核心作用 跨网络通信&#xff1a;连接不同网络&#xff08;如LAN与WAN、不同子网&#xff09;&#xff0c;基于IP地址进行数据包转发。 路由决策&#xff1a;通过路由协议&#xff08;如OSPF、BGP&#xff09;动态选择最优路径&…

LangChain 核心技术

以下是基于我简历中的项目经验,结合LangChain技术栈整理的常见面试题及针对性回答建议。这些问题覆盖了技术实现、项目设计、创新点和行业应用等方面: 一、LangChain 核心技术相关问题 1. 请解释 LangChain 的核心设计理念和主要组件。 考察点:对LangChain框架的理解深度。…

深度解读 AWS IAM:身份访问管理与安全的核心纽带

导语 在 AWS&#xff08;亚马逊云服务&#xff09;的生态体系中&#xff0c;AWS IAM&#xff08;Identity and Access Management&#xff09;犹如坚固的堡垒&#xff0c;守护着用户在云端的各类资源。它不仅是管理用户身份与访问权限的关键工具&#xff0c;更是维系 AWS 安全…

【AI论文】挑战推理的边界:大型语言模型的数学基准测试

摘要&#xff1a;近年来&#xff0c;大型推理模型的迅猛发展导致现有用于评估数学推理能力的基准测试趋于饱和&#xff0c;这凸显出迫切需要更具挑战性和严谨性的评估框架。为填补这一空白&#xff0c;我们推出了OlymMATH&#xff0c;这是一项全新的奥林匹克级数学基准测试&…

VS2022 Qt 项目使用数据库报错问题

一、问题现象&#xff1a;无法解析的外部符号 "__declspec(dllimport) public: __cdecl QSqlDatabase::QSqlDatabase(void)" 定义变量QSqlDatabase db后报错信息为“无法解析的外部符号” 二 、解决步骤&#xff1a; 1、在 Visual Studio 2022 中&#xff1a;右键项…

uv命令介绍(高性能Python包管理工具,旨在替代pip、pip-tools和virtualenv等传统工具)

文章目录 **主要功能**1. **快速安装和管理 Python 包**2. **生成和管理锁文件 (requirements.lock)**3. **创建虚拟环境**4. **与 poetry 兼容** **核心优势**1. **极快的速度**&#xff1a;基于 Rust 实现&#xff0c;利用多线程和缓存大幅加速依赖解析。2. **轻量且独立**&a…

wait和notify : 避免线程饿死(以及votile内存可见性和指令重排序问题)

各位看官&#xff0c;大家早安午安晚安呀~~~ 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦 今天我们来学习&#xff1a;wait和notify : 避免线程饿死&#xff08;以及votile内存可见性和指令重排序问题&#xff09; …