c++20中的formatting

一、c++中的I/O流

在传统的c语言编程中,程序员们可能对printf情有独衷,至少在调试时可以打个Log,以方便测试是否和实际的设计逻辑保持一致。但是这个printf其实是有不少的问题和安全隐患的,所以在c++中提供了iostream这个类,但是说实话,这个类好像还不如printf用得更好。很多人对这个类表示了极大的不友好,但是c++一直对流处理没怎么做更强大的升级,反正搞到c++这个地界儿的,基本都不是什么善茬。
但是c++11大幅升级c++标准以来,在c++20中,终于对此有了一些具体的改变,增加了formatting这个库,下面就介绍一下这个库的应用。

二、formatting

c++20中的formatting这个格式化库,其实是对printf族函数的一个替代方案,同时,也对STL中的IO流有一个补充。其实,有过其它语言开发的程序员可能一眼就看出来,它和c#等语言的Format没有什么不同,一直是编程语言发展的方向,即应用简单化、明确化。给程序员以一个高效、安全的应用库。看一下它的基本定义:

//format
template< class... Args >
std::string format( /*format_string<Args...>*/ fmt, const Args&... args );
template< class... Args >
std::wstring format( /*wformat_string<Args...>*/ fmt, const Args&... args );
template< class... Args >
std::string format( const std::locale& loc,
                    /*format_string<Args...>*/ fmt, const Args&... args );
template< class... Args >
std::wstring format( const std::locale& loc,
                     /*wformat_string<Args...>*/ fmt, const Args&... args );
//vformat
std::string vformat(std::string_view fmt, std::format_args args);
std::wstring vformat(std::wstring_view fmt, std::wformat_args args);
std::string vformat(const std::locale& loc, std::string_view fmt, std::format_args args);
std::wstring vformat(const std::locale& loc, std::wstring_view fmt, std::wformat_args args);

formatting提供了下列函数:
在这里插入图片描述

formatting还提供了扩展性的支持:
在这里插入图片描述

下面看几个简单的例子,这里面有几句话特别有意思,先看一下文档上怎么说的:

fmt-未指定类型的形参,其初始化仅若实参可转换成 std::string_view (对于 (1,3) )或 std::wstring_view (对于 (2,4) ),而转换结果是常量表达式和 Args 的合法格式字符串才合法。格式字符串由以下内容组成:
通常字符(除了 { 与 } ),它们被不加修改地复制到输出,
转义序列 {{ 与 }} ,它们在输出中被分别替换成 { 与 } ,以及
替换域。
每个替换域拥有下列格式:

引入的 { 字符;
(可选) arg-id ,一个非负数;
(可选) 冒号( : )后随格式说明;
终止的 } 字符。
arg-id 指定用于格式化其值的 args 中的参数的下标;若省略 arg-id ,则按顺序使用参数。格式字符串中的 arg-id 必须全部存在或全部被省略。混合手动和自动指定下标是错误。

格式说明由对应参数特化的 std::formatter 定义。

对于基本类型和标准字符串类型,格式说明为标准格式说明;
对于标准日期和时间类型,格式说明为 chrono 格式说明;
对于用户定义类型,格式说明由用户定义的 std::formatter 特化决定。”

再深入看格式定义中又有一句话:
“标准格式说明:对于基本类型和字符串类型,格式说明基于 Python 中的格式说明”。

三、例程

下面看几个例子:

#include <format>
#include <iostream>
#include <string>
#include <string_view>
 
template <typename... Args>
std::string dyna_print(std::string_view rt_fmt_str, Args&&... args) {
    return std::vformat(rt_fmt_str, std::make_format_args(args...));
}
 
int main() {
    std::cout << std::format("Hello {}!\n", "world");
 
    std::string fmt;
    for (int i{}; i != 3; ++i) {
        fmt += "{} "; // 构造格式化字符串
        std::cout << fmt << " : ";
        std::cout << dyna_print(fmt, "alpha", 'Z', 3.14, "unused");
        std::cout << '\n';
    }
}

运行结果:

Hello world!
{}  : alpha 
{} {}  : alpha Z 
{} {} {}  : alpha Z 3.14

再看一个formatted_size的例子:

#include <format>
#include <iostream>
#include <vector>
#include <string_view>

int mainty()
{
    using namespace std::literals::string_view_literals;

    constexpr auto fmt_str{ "Hubble's H{0} {1} {2:*^4} miles/sec/mpc."sv };
    constexpr auto sub_zero{ "₀"sv };  // { "\u2080"sv } => { 0xe2, 0x82, 0x80 };
    constexpr auto aprox_equ{ "≅"sv }; // { "\u2245"sv } => { 0xe2, 0x89, 0x85 };
    constexpr int Ho{ 42 }; // H₀


    const auto min_buffer_size = std::formatted_size(fmt_str, sub_zero, aprox_equ, Ho);

    std::cout << "Min buffer size = " << min_buffer_size << '\n';

    // 以 std::vector 为动态缓冲区。注:缓冲区不包含尾随 '\0' 。
    std::vector<char> buffer(min_buffer_size);

    std::format_to_n(buffer.data(), buffer.size(), fmt_str, sub_zero, aprox_equ, Ho);

    std::cout << "Buffer: \"" << std::string_view{ buffer.data(), min_buffer_size } << "\"\n";

    // 或者我们能通过添加尾随 '\0' 直接打印缓冲区。
    buffer.push_back('\0');
    std::cout << "Buffer: \"" << buffer.data() << "\"\n";
    return 0;
}
int main()
{
    mainty();
}

运行结果如下:

Min buffer size = 33
Buffer: "Hubble's H? ? *42* miles/sec/mpc."
Buffer: "Hubble's H? ? *42* miles/sec/mpc."

这里需要说明一下,std::literals::string_view_literals::operator""sv()是一个重载函数,返回一个std::string_view类型,具体的可参看:
https://en.cppreference.com/w/cpp/string/basic_string_view/operator%22%22sv

format_to_n的例程:

#include <format>
#include <string_view>
#include <iostream>
 
int main()
{
    char buffer[64];
 
    const auto result =
        std::format_to_n(buffer, std::size(buffer), 
                         "Hubble's H{0} {1} {2} miles/sec/mpc.",
                         "\u2080", "\u2245", 42);
 
    std::cout << "Buffer: \"" << std::string_view{buffer, result.size} << "\"\n"
              << "Buffer size = " << std::size(buffer) << '\n'
              << "Untruncated output size = " << result.size << '\n';
}

运行结果:

Buffer: "Hubble's H₀ ≅ 42 miles/sec/mpc."
Buffer size = 64
Untruncated output size = 35

在c++20的文档目前还不太全,包括在vs2022上开发时,指定了c++20标准仍然无法完全编译,还是得指定最新的标准才行,这点还是需要注意。在Linux上编译上最新的gcc,但其它的一些工程和调试啥的都得更新,这样就无法展开工作了,先在Win平台上应用吧。

四、总结

c++标准的进步还是非常明显的,可能对于一些大牛觉得啥也不是,但对于普通的广大程序员还是大有好处的。一个技术的出现,总会有人说好有人说坏,根据自己的实际情况,来决定是不是需要它,应用它,这才是最好的。但是前提是,得有这个判断的能力和水平。不断的提高,不断的拥抱变化,才是根本。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值