c++23中的新功能之十二格式化输出

文章介绍了C++中格式化输出的演变,从printf到流式输入输出,再到C++20的std::format和C++23的std::print。std::format作为printf的替代,虽好用但功能有限,不支持某些类型和文件操作。C++23的std::print引入以支持更多容器和Ranges,但自定义类型仍需额外工作。文章讨论了如何处理自定义类的格式化输出,并指出目前的解决方案仍待完善。
部署运行你感兴趣的模型镜像

一、介绍

说起格式化输出那真是一言难尽,从汇编到C,到c++,到c#、Java、Python、Go…哪个不是各出手段,尽显风骚。搞c++开发的,好多人其实用的不外乎是printf。可是这个函数有个致命的问题,它只能打印他自己能玩儿的东西,想换个他不知道的,好,做梦吧。
而c++在后来也觉得它用着不爽,毕竟我是带类的C,你不能打印类,那不等于大多数的工作没法完成。没办法,只好自己搞了个流式输入输出,重载一下operator<<,小日子和美美的过下去了。然而,肯定会有然而。
然而就是,STL中有大量的容器,可你不支持,举个简单例子,有一个std::vector,直接<<vec不可以,换句话说不支持Ranges,可这个是c++20的重大特征啊。所以出现了std::format,可由于各种原因,这个东西好用是好用,可功能面太窄了,竟然不支持流时代支持的文件操作,甚至原来流支持的它也不支持(std::complex,而且仍然对Ranges是未来支持的态度)。
这就出现了两个情况:一个是继续完善std::format,一个是推出了std::print(或者是println啥,看最后标准定义)。那么流(std::cout及其它)呢?默认可能会被淘汰吧。

二、c++20的std::format

printf和流的就不用再写了,大家都用得非常熟稔。先从c++20的std::format开始。
先看一个cppreference.com上的例子:

#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 += "{} "; // constructs the formatting string
        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

在std::formatter中默认已经对基本的数据类型进行了处理,但要是处理类似于自定义类的方式有两种方式来解决,一种是继承std::formatter来实现;另外一种就是自己实现parse和format两个函数。
看一下前者的例子:
先看cppreference提供的:

#include <format>
#include <iostream>

// 类型 T 的包装
template<class T>
struct Box
{
    T value;
};

// 能用被包装值的格式说明格式化包装 Box<T>
template<class T, class CharT>
struct std::formatter<Box<T>, CharT> : std::formatter<T, CharT>
{
    // 从基类继承 parse()

    // 通过以被包装值调用基类实现定义 format()
    template<class FormatContext>
    auto format(Box<T> t, FormatContext& fc)
    {
        return std::formatter<T, CharT>::format(t.value, fc);
    }
};

int main()
{
    Box<int> v = {42};
    std::cout << std::format("{:#x}", v);
}

再照着来一个:

template <typename _T1, typename _T2>
struct ABC {
	_T1 v1;
	_T2 v2;
};

template <typename _T1, typename _T2, typename _CharT>
struct std::formatter<ABC<_T1, _T2>, _CharT> : std::formatter<_T1, _CharT>
{	 
	template <typename _FormatContext>
	auto format(const ABC<_T1, _Ty2>& v, _FormatContext& format_context )
	{
		auto it = std::formatter<_T1, _CharT>::format(v.v1, format_context);
		it = '\n';
		it = std::formatter<_T2, _CharT>().format(v.v2, format_context);
		return it;
	}
};


#include <iostream>
void Test()
{
	ABC<int, double> abc{
	.v1 = 1,
		.v2 = 2.1
	};
	std::cout << std::format("abc = {}", abc);
}
int main()
{
	Test();
	return 1;
}

第二种大家有兴趣可以自己搞搞,没觉得有如operator<<简单。
更多请参看:
https://zh.cppreference.com/w/cpp/utility/format/formatter

三、c++23中的std::print

而在新标准中,对关联容器和非关联容器都有不同的处理方式,一般来说关联容器比较简单,直接输了就可以了,但对于非叛逆容器,如Map和Set系列等,可能对Ranges的输了不感冒,那么就需要象上面一样自己来定义一些输出格式。另外对于一些特殊形式,比如char数组到string,一些无法打印的类型这些都需要进行处理。最后,大家都知道,在STL还有一种容器适配器的存在,它们怎么处理?
所以在目前提出的解决方式中提出了规范:需要输入Range,元素类型可格式化并且元素类型不能是Range自己。当然还对视图以及一些引用形式及容器适配器都做了规范。比如默认就是格式化成[item0,item1…],使用方括号。而std::pair,std::tuple则是使用小括号,其它的可以参看最新的标准出来。
除此之外,std::println还提供了一些类似于正则内部的一些参数,可以去除一些内容,比如?可以忽视的调试符,n,去除外包装(就是外面那对括号),诸如种种,以最终标准及编译器厂商为准。
看一个例子:

std::println("{:?}", std::pair{3, 3}); // (3, 3)
std::println("{:n}", std::pair{3, 3}); // 3, 3
std::println("{:m}", std::pair{3, 3}); // 3: 3

同样,这些都是对于STL自带Ranges的容器,如果是自定义的呢?还是需要std::range_formatter来自定义元素进行格式化。My God!

四、总结

反正在这个方面上,可能看上去简单了,但目前还没法体现出简单来。估计大佬们也在挠头。让他们去慢慢折腾吧,等着最后的结果就行。能用就用,不能用就暂时绕着呗。反正c++23到工程上用,肯定还有一大段时间。目前工程上能用到c++17的就已经是很潮的公司了。

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think> 我们注意到用户的问题是关于C++中text的格式以及如何进行格式化输出。虽然之前的引用中提到了Sublime Text的代码格式化C++的字符格式化输出,但我们需要聚焦在C++语言本身的文本处理和格式化输出上。 用户的问题可以分为两部分: 1. C++中text的格式(可能指的是文本数据的存储和表示方式) 2. 如何对text进行格式化输出C++中,文本通常以字符串(string)的形式表示。字符串是字符的序列,可以存储和处理文本数据。格式化输出则是指按照特定格式要求输出文本。 C++提供了多种方式来处理字符串的格式化输出: 1. 传统的C风格printf家族函数(虽然不推荐在C++中大量使用,但仍有其用途) 2. C++的iostream库(使用操作符<<进行输出,配合流操纵符控制格式) 3. C++20引入的新的格式化库(std::format),它提供了一种类似于Python中format的现代方式 下面我们逐一解释: ### 一、C++中文本的表示 在C++中,文本通常使用以下两种方式表示: 1. **C风格字符串**:字符数组,以空字符'\0'结尾 ```c++ const char* cstr = "Hello World"; ``` 2. **C++std::string类**(更安全、更常用) ```c++ #include <string> std::string str = "Hello World"; ``` ### 二、文本格式化输出方法 #### 方法1:使用C++ iostream库(最常用) 通过`<iostream>`和`<iomanip>`头文件提供的功能进行格式化输出。 示例1:基本输出 ```c++ #include <iostream> int main() { std::string name = "Alice"; int messages = 5; std::cout << "Hello, " << name << "! You have " << messages << " new messages." << std::endl; return 0; } ``` 示例2:设置输出格式(如宽度、填充、精度) ```c++ #include <iostream> #include <iomanip> int main() { double pi = 3.1415926535; // 设置浮点数精度为3 std::cout << std::setprecision(3) << pi << std::endl; // 输出3.14 // 设置输出宽度为10,用*填充 std::cout << std::setw(10) << std::setfill('*') << 123 << std::endl; // 输出*******123 return 0; } ``` #### 方法2:使用C风格的printf(需要包含<cstdio>) ```c++ #include <cstdio> #include <string> int main() { std::string name = "Bob"; int messages = 3; // 注意:printf不能直接输出std::string,需转换为C字符串 printf("Hello, %s! You have %d new messages.\n", name.c_str(), messages); return 0; } ``` #### 方法3:使用C++20的std::format(需要编译器支持C++20) C++20引入了新的格式化库,位于`<format>`头文件中,使用类似Python的格式化语法。 示例: ```c++ #include <iostream> #include <format> int main() { std::string name = "Charlie"; int messages = 7; // 格式化字符串 std::string formatted = std::format("Hello, {}! You have {} new messages.", name, messages); std::cout << formatted << std::endl; return 0; } ``` 注意:目前并非所有编译器都完全支持std::format,如果编译器不支持,可以使用第三方库(如fmt库)作为替代。 ### 三、高级文本格式化技巧 - 使用流操纵符控制输出格式(十六进制、八进制、科学计数法等) - 使用字符串流(ostringstream)将格式化结果保存到字符串中 ```c++ #include <sstream> #include <iomanip> int main() { std::ostringstream oss; oss << std::setprecision(5) << 3.14159; std::string pi_str = oss.str(); // 得到"3.1416" return 0; } ``` ### 四、文本处理中的转义字符 在字符串中,特殊字符需要通过转义序列表示: - `\n`:换行 - `\t`:制表符 - `\\`:反斜杠 - `\"`:双引号 例如: ```c++ std::cout << "This is a quote: \" and a new line:\nNext line" << std::endl; ``` ### 总结 - 在C++中,文本通常用`std::string`表示 - 格式化输出可以使用: 1. iostream库(推荐,类型安全,扩展性好) 2. printf(与C兼容,但不直接支持std::string) 3. C++20的std::format(现代方式,但需要编译器支持) 根据实际需求和编译器支持情况选择合适的方法。 [^4]: C++字符格式化输出示例 [^5]: C++20 format格式化输出 §§相关问题§§ 1. C++20的std::format支持哪些高级格式化特性? 2. 如何在较旧的C++版本中实现类似std::format的功能? 3. 如何安全地在C++中处理包含特殊字符的文本? </think> 在C++中,文本通常以字符串(`std::string`)形式表示,格式化输出则指控制文本的显示格式(如对齐、精度、进制等)。以下是核心知识点和具体方法: --- ### 一、C++中的文本格式 1. **基础表示**: ```cpp #include <string> std::string text = "Hello, C++!"; // UTF-8编码的字符串 ``` 2. **特殊字符处理**(需转义): ```cpp std::string escaped = "Line1\\nLine2\\tTab\\"Quote\\""; // \n=换行, \t=制表符 ``` 3. **原始字符串**(C++11起): ```cpp std::string raw = R"(Raw \n remains as text)"; // 输出:Raw \n remains as text ``` --- ### 二、文本格式化输出的4种方法 #### 1. **使用`iostream`库**(基础) ```cpp #include <iostream> #include <iomanip> // 格式控制 int main() { double pi = 3.1415926535; std::cout << std::fixed << std::setprecision(2) // 固定小数,保留2位 << "Pi: " << pi << std::endl; // 输出 Pi: 3.14 } ``` - **常用控制符**: - `std::setw(n)`:字段宽度为n字符 - `std::setfill(c)`:填充字符(默认空格) - `std::left`/`std::right`:左/右对齐 #### 2. **C风格`printf()`**(需包含`<cstdio>`) ```cpp #include <cstdio> int main() { char name[] = "Alice"; printf("Name: %-10s | Age: %03d\n", name, 25); // 输出:Name: Alice | Age: 025 } ``` - **格式说明符**: - `%s`:字符串 - `%d`:整数 - `%f`:浮点数 - `%-10s`:左对齐且宽度10 #### 3. **C++20的`std::format`**(现代方式) ```cpp #include <format> #include <iostream> int main() { std::string msg = std::format("Hello, {}! π ≈ {:.2f}", "Bob", 3.14159); std::cout << msg; // 输出:Hello, Bob! π ≈ 3.14 } ``` - **优势**: - 类型安全(编译期检查) - 支持位置参数:`{0}`, `{1}` - 格式规范:`{:.2f}`表示保留2位小数 #### 4. **第三方库fmt**(兼容旧标准) 若编译器不支持C++20,可使用[fmt库](https://github.com/fmtlib/fmt): ```cpp #include <fmt/core.h> int main() { fmt::print("Temperature: {:+06.1f}°C", -5.5); // 输出:Temperature: -005.5°C } ``` --- ### 三、代码编辑器中的格式化工具 结合编辑器(如Sublime Text)提升效率: 1. **安装Clang-Format**: ```bash # Windows scoop install llvm # 包含clang-format ``` 2. **Sublime Text集成**: - 安装插件:Package Control → 搜索`Clang-Format` - 快捷键:`Ctrl+Alt+F` 格式化当前文件[^2][^3] --- ### 四、应用场景示例 ```cpp #include <iostream> #include <iomanip> #include <string> int main() { std::string product = "Laptop"; double price = 1299.99; int stock = 50; // 表格化输出 std::cout << std::left << std::setw(15) << "Product" << std::setw(10) << "Price" << "Stock\n" << std::string(30, '-') << '\n' << std::setw(15) << product << "$" << std::fixed << std::setprecision(2) << std::setw(9) << price << stock << std::endl; } ``` **输出**: ``` Product Price Stock ------------------------------ Laptop $1299.99 50 ``` --- ### 关键点总结 | 方法 | 优点 | 缺点 | |---------------|--------------------------|-----------------------| | `iostream` | 类型安全,C++原生支持 | 语法冗长 | | `printf()` | 简洁,控制灵活 | 类型不安全 | | `std::format` | 现代语法,类型安全 | 需C++20支持 | | fmt库 | 兼容旧标准,功能强大 | 需额外安装 | [^2]: Windows下Sublime Text 3使用clang-format [^3]: Sublime Text自定义clang-format插件 [^5]: C++20 format格式化输出
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值