1、格式化转义字符与字符串
怎么把转义字符或转义字符串输出来,不让它转义,原样输出来,你可能觉得奇怪,这是要干什么?这个确实有用的,比如在程序调试的时候,我想把某一段含有转义字符或者转义字符串的内容输出出来。用法也是比较简单的,就是使用?类型说明符,其实在上一篇文章中也提到了,只是没有进行解释,下面我们看一下常用的转义字符吧:
字符 | 转义输出 |
---|---|
水平制表符 | \t |
换行 | \n |
回车 | \r |
反斜杆 | \\ |
双引号 | \" |
单引号 | \' |
还有一点,要单独的“两边要用',要单独的'两边要用",这个也比较好理解吧,看示例:
import std;
using namespace std;
int main()
{
println("|{:?}|", "Hello\tWorld!\n");
println("|{:?}|", "\"");
println("|{:?}|", '\'');
println("|{:?}|", '"');
println("|{:?}|", "'");
return 0;
}
结果如下:
|"Hello\tWorld!\n"|
|"\""|
|'\''|
|'"'|
|"'"|
2、格式化范围Range
在C++中,std::vector,array,pair会保存多个数据,在标准库容器中,还会有这样的容器,我们要直接将这样的元素输出出来,这就是本节要讨论的话题,可能会比较复杂,要集中一下思想,否则可能会跟不上。像vector,array这样的范围Range,缺省输出是有[]符号的,其元素是由,分隔的,原样输出,这比较好理解,当然了,对于元素类型为字符串的,如果字符串中包含转义字符,缺省是会进行转义的。
范围Range格式化的说明符如下:
[[fill]align][width][n][range-type][:range-underlying-spec]
所有[]中的内容都可以省略,前面几节我们也介绍了格式化字符串的一些内容,这里面的一些说明符与前面介绍的大同小异,本次就简单地一带而过了。fill指定了填充符,align指定了对齐方式,width指定了输出的宽度,如果指定了n,那么输出中就省略掉了范围Range两边的[],range-type解释如下:
- m:只针对于带两个元素的pair与tuple有效,缺省是由,分隔括号包围的,如果指定了m,就不用括号了,分隔符也变成了:
- s:将范围Range格式化为字符串(不与n或者range-underlying-spec同时使用)
- ?s:将范围Range格式化为转义字符串(不与n或者range-underlying-spec同时使用)
range-underlying-spec是范围Range元素的可省略的格式说明符。范围Range说明符支持多层嵌套,如果范围Range的元素又是一个范围Range(例如Vector的元素也是Vector),那么range-underlying-spec也是另一个范围Range的格式化说明符,以此类推。
还是通过示例来学习吧,可能更容易理解一些:
先看一个元素为数字的Vector:
vector values { 11, 22, 33 };
println("{}", values); // [11, 22, 33]
println("{:n}", values); // 11, 22, 33
默认输出就是以[]包围的以,分隔的三个元素的vector,与定义完全一致,:n就是把包围的[]去掉,再继续往下看,如果你只是想把[]替换为其他的符号,那就要与:n结合使用,看示例:
println("{{{:n}}}", values); // {11, 22, 33}
该示例就是将[]替换为{}的代码,其中:n去掉了[],替换成{},由于{}与指示符冲突,所以要用{{与}}来代替。再看如下的例子:
println("{:*^16}", values); // **[11, 22, 33]**
println("{:*^16n}", values); // ***11, 22, 33***
*为填充符,^为居中对齐符,16为宽度;第二行有n,就将[]去除掉了,继续学习:
println("{::*^6}", values); // [**11**, **22**, **33**]
由于多加了一个:,那就是对各个元素进行格式化了,将各个元素以*为填充符,宽度为6,居中,再继续:
println("{:n:*^6}", values); // **11**, **22**, **33**
外层加上n,也就把[]去除掉了,go on,这次我们把元素换成字符串:
vector strings { "Hello"s, "World!\t2023"s };
println("{}", strings); // ["Hello", "World!\t2023"]
println("{:}", strings); // ["Hello", "World!\t2023"]
println("{::}", strings); // [Hello, World! 2023]
println("{:n:}", strings); // Hello, World! 2023
第一行、第二行都是默认输出,第三行对vector的字符串元素进行转义输出,由于第二个字符串中有\t,被转义成了一个制表符的正常输出,第四行外围加了n,就将[]去除掉了。
再举一个元素为字符的例子:
vector chars { 'W', 'o', 'r', 'l', 'd', '\t', '!' };
println("{}", chars); // ['W', 'o', 'r', 'l', 'd', '\t', '!']
println("{::#x}", chars); // [0x57, 0x6f, 0x72, 0x6c, 0x64, 0x9, 0x21]
println("{:s}", chars); // World !
println("{:?s}", chars); // "World\t!"
第一行正常输出,第二行对各个元素以0x开头转换成16进制进行输出,第三行将字符组合成字符串进行转义输出,第四行在第三行的基础上不转义。
再看一个有两个元素的pair的例子:
pair p { 11, 22 };
println("{}", p); // (11, 22)
println("{:n}", p); // 11, 22
println("{:m}", p); // 11: 22
第一行正常输出,第二行将两边的()去掉,第三行在第二行的基础上将,替换成:进行分隔。
最后再看一个嵌套vector的例子:
vector<vector<int>> vv { {11, 22}, {33, 44, 55} };
println("{}", vv); // [[11, 22], [33, 44, 55]]
println("{:n}", vv); // [11, 22], [33, 44, 55]
println("{:n:n}", vv); // 11, 22, 33, 44, 55
println("{:n:n:*^4}", vv); // *11*, *22*, *33*, *44*, *55*
第一行正常输出,第二行将最外围的[]去掉,第三行将外围及里面的[]都去掉,第四行在第三行的基础上对元素的子元素进行格式化,以*填充,宽度为4,居中。