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

发布于:2025-04-01 ⋅ 阅读:(20) ⋅ 点赞:(0)

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


网站公告

今日签到

点亮在社区的每一天
去签到