当前位置: 首页 > news >正文

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

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.dtcms.com/a/98222.html

相关文章:

  • 每日一题 == 674. 最长连续递增序列
  • 26_ajax
  • qgis点从面图层上拾取属性
  • NLP高频面试题(二十四)——RAG相关内容简介
  • 【Java】public class Person{}和public Person{} 和 public Person person究竟都有哪些区别呢。
  • Java---类与对象
  • NLP高频面试题(二十三)对抗训练的发展脉络,原理,演化路径
  • 关于跨域与.NET的处理方案
  • 软考-高级-系统架构设计师【考试备考资料下载】
  • 自学-408-《计算机网络》(总结速览)
  • 区块链在教育领域的创新应用:改变传统教育的未来
  • 黑盒测试的等价类划分法(输入数据划分为有效的等价类和无效的等价类)
  • 综合实验
  • qt之使用redis与其他程序(python)交互同通信
  • 基于SpringBoot实现的高校实验室管理平台功能四
  • 多线程 -- Thread类
  • vue学习
  • Linux内核同步机制:解锁并发编程的奥秘
  • 软件的常用设计模式。可参考一个一个学习
  • 用Nginx实现负载均衡与高可用架构(整合Keepalived)
  • [Linux]在vim中批量注释与批量取消注释
  • 进程Kill杀死后GPU显存没有释放仍然被占用,怎么杀死僵尸进程
  • 跟着StatQuest学知识08-RNN与LSTM
  • Claude 在 SVG 绘图创作中的潜力与技巧
  • 【软考-架构】10.1、软件工程概述-CMM-软件过程模型-逆向工程
  • Pycharm (十)字符串扩展:统计小串在大串中出现的次数
  • C++23:现代C++的模块化革命与零成本抽象新高度
  • 笔记:遇见未来——6G协同创新技术研讨会
  • FPGA调试笔记
  • 从代码学习深度学习 - 含并行连结的网络(GoogLeNet)PyTorch版