编程技能:格式化打印05,格式控制符
专栏导航
本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏,故划分为两个专栏导航。读者可以自行选择前往哪个专栏。
(一)WIn32 专栏导航
上一篇:编程技能:格式化打印04,sprintf
回到目录
下一篇:无
(二)MFC 专栏导航
上一篇:编程技能:格式化打印04,sprintf
回到目录
下一篇:无
本节前言
在前几节,我们讲解了 vsprintf,printf 与 sprintf 函数。如果,这几节,你学得很好,那么,对于格式控制符,格式字符串等等的概念,你就会有一定的了解了。同时,对于可变参数啥的,你也会有一定的了解。你也会了解到 printf 与 vsprintf 的互动机制,了解到 sprintf 与 vsprintf 的互动机制。
以上的内容,我认为,都是很重要的。
然而,在讲解以上几节内容的时候,我并未对格式控制符详细展开。本节,我也并不打算详细地讲解格式控制符的每一个知识点。然而,我还是想要多讲解一点关于格式控制符的知识。
本节,将作为格式化打印版块儿的最后一节。希望大家能够学好本节。
本节结束了以后,我们将会进入正式的 Windows 编程知识的讲解。无论你是进入 Win32 知识的学习,还是进入 MFC 知识的学习。
我们开始本节的讲解。
一. 什么是格式控制符?
格式控制符,是我在我的专栏里面所采用的概念。通行的术语是什么,我记不清了。此刻,我也不想去专门查阅这个东西。
在我的专栏里,我将 printf,sprintf 函数里面的 %d,%c,%f,%s 等等的东西,叫做格式控制符。
例如,代码【printf("a + b = %d\n", a + b);】里面,%d 便是格式控制符。%d 表明,在输出打印的时候,%d 所在的位置,应该是一个 int 型的数据。
在基本的 C 语言学习里面,一般地,我们只是在使用着格式控制符,而并未对其进行专门的学习。
格式控制符,除了基本的 %d,%c,%s,%f 等等的东西以外,其实还包括其他的一些个有趣的东西的。
在本节,我打算讲解与浮点数无关的一些个格式控制符的知识。因为,水平所限,让我讲浮点型的,此时我也讲不了。
在格式控制符里面,比较简单的组成,是左边一个 %,右边一个 d,c,s 等等的字符。
除了这样的简单组成以外,其实格式控制符还可以有其他的部分。
与浮点数无关的格式控制符,还可以包含有许多的字段与标志。可以包含的标志有,填零标志,符号标志,正号标志,空格标志,特殊显示标志,左对齐标志,大小写标志。可以包含的字段有,宽度字段,精度字段。
接下来,我来尝试着讲解一下这些个字段与标志。为了方便讲解,我首先来讲解字段,然后再去讲解标志。
二. 宽度字段
字段,我初次接触这个概念,是在学习数据库的时候。字段,英文为 field 。对于 field,有的人将其翻译为【字段】,有的人将其翻译为【域】。所以,【宽度字段】,有的人也将其称作【宽度域】。其它的,涉及【字段】这个概念的,你都可以将【某某字段】其叫做【某某域】。
我下定义的能力,有时候并不强。所以,对于宽度字段这个概念,我用举例子的方式来加以说明。
比如,代码【printf("小丽的口袋里有%d块钱", 37);】,这段代码里,不包括宽度字段。我们给它加上一个宽度字段。加上宽度字段之后,代码为【printf("小丽的口袋里有%6d块钱", 37);】。
看到了没?在 % 与 d 之间,加上了一个数字 6,这个数字 6,便是属于宽度字段。当然了,你也可以不使用 6 这个数,你也可以使用 9,7,12 等等的整数。总之呢,在 % 和 d 之间,直接加上一个整数,中间不含空格,这个所加的整数,便是宽度字段。
它的执行效果,我们可以给出一个示例代码。
#include <stdio.h>int main(void)
{printf("小丽的口袋里有%d块钱\n", 37);printf("小丽的口袋里有%3d块钱\n", 37);printf("小丽的口袋里有%6d块钱\n", 37);printf("小丽的口袋里有%9d块钱\n", 37);return 0;
}
运行结果如下。

大家看到了吧?
在不加宽度字段的时候,%d 的位置,在打印输出时,直接被转换为【37】这个整数。【37】这个这个数,本身占用的是 2 个字符的宽度。
在 %3d 里面,转换成的整数依旧是 37,它本身依旧是占用 2 个字符的宽度。然而,却在 37 这个数的左边,加了一个空格。一个空格占用的是 1 个字符的宽度。加上去的占用 1 个字符宽度的 1 个空格,与占用 2 个字符宽度的 37,合起来,一共是占用了 3 个字符宽度。
在 %6d 里面,转换成的整数依旧是 37,它本身依旧是占用 2 个字符的宽度。然而,却在 37 这个数的左边,加了 4 个空格。一个空格占用的是 1 个字符的宽度。加上去的占用 4 个字符宽度的 4 个空格,与占用 2 个字符宽度的 37,合起来,一共是占用了 6 个字符宽度。
在 %9d 里面,转换成的整数依旧是 37,它本身依旧是占用 2 个字符的宽度。然而,却在 37 这个数的左边,加了 7 个空格。一个空格占用的是 1 个字符的宽度。加上去的占用 7 个字符宽度的 7 个空格,与占用 2 个字符宽度的 37,合起来,一共是占用了 9 个字符宽度。
%3d,使得转换后的东西,占用了 3 个字符的宽度。%6d,使得转换后的东西,占用了 6 个字符的宽度。%9d,使得转换后的东西,占用了 9 个字符的宽度。
原本,我们要替换的数,是37,它只占用 2 个字符的宽度。那么,对于 %3d,在数字 37 所占用的 2 个字符宽度之外,需要添加 1 个空格。对于 %6d,在数字 37 所占用的 2 个字符宽度之外,需要添加 4 个空格。对于 %9d,在数字 37 所占用的 2 个字符宽度之外,需要添加 7 个空格。
这样一来,对于宽度字段,以及所添加的空格数的关系,大家应该就能够明白了吧?
宽度字段,是建议转换后的东西,一共占用多少个字符的宽度。原本的数字,若不够这个宽度的话,默认地,会填上空格,以凑成宽度字段所要求的宽度。
不过,在以上的例子中,我们给出的宽度,是大于数字 37 本身所占用的 2 个字符的宽度值的。如果,我们给出的,是 %1d,会如何呢?
宽度字段的值 1,小于数字 37 本身的宽度值 2,那么,结果是,37 这个数字正常输出,%1d 中的宽度字段 1 不起作用。
同理地,对于【printf("%3d", 123456);】,由于 123456 占用的字符宽度值为 6,而 %3d 中的宽度字段值为 3,3 小于 6,所以呢,宽度字段值 3 不起作用,实际输出的时候,123456 依旧会正常地输出。
但是呢,如果是【printf("%9d", 123456);】,123456 这个数占用的字符宽度值为 6,而 %9d 中的宽度字段值为 9,由于 9 大于 6,所以,实际打印输出的时候,%9d 的位置,被转换后的内容,真的会占用 9 个字符的宽度的。此时,由于 123456 这个数已经占用了 6 个字符,所以呢,为了凑成 9 个字符的宽度,只需要再添加 3 个空格就好了。
到了这里,对于宽度字段,大家应该是懂了吧?
三. 精度字段
我们还是以小丽口袋里的钱来说事。我们给出如下的代码。
#include <stdio.h>int main(void)
{printf("小丽的口袋里有%d块钱\n", 37);printf("小丽的口袋里有%.3d块钱\n", 37);printf("小丽的口袋里有%.6d块钱\n", 37);printf("小丽的口袋里有%.9d块钱\n", 37);return 0;
}
运行结果如下。

看懂了没?在 % 与 d 之间,加上一个【.x】,其中的 x 为一个整数,这个【.x】,就是精度字段。【x】是几,就表示说,让转换后的整数占用多少个字符精度,若是整数本身的宽度不足精度数的,则补 0 。
%.3d 是建议,转换后的数字,要占用 3 个字符精度,而待转换的数字 37 只占用 2 个字符宽度,所以呢,左边补上 1 个 0 。
%.6d 是建议,转换后的数字,要占用 6 个字符精度,而待转换的数字 37 只占用 2 个字符宽度,所以呢,左边补上 4 个 0 。
%.9d 是建议,转换后的数字,要占用 9 个字符精度,而待转换的数字 37 只占用 2 个字符宽度,所以呢,左边补上 7 个 0 。
那么,如果精度数小于待转换数字本身占用的字符宽度,这会如何呢?与宽度字段类似,此时,精度字段不起作用,待转换数字会正常地输出,相当于单纯的 %d,精度字段会被忽略掉。
例如,对于【printf("%.3d", 123456);】,由于 123456 占用的字符宽度值为 6,而 %.3d 中的精度字段值为 3,3 小于 6,所以呢,精度字段值 3 不起作用,实际输出的时候,123456 依旧会正常地输出,相当于单纯的 %d,精度字段被忽略掉。
四. 宽度字段与精度字段连用
啊,我给出一段代码。
#include <stdio.h>int main(void)
{printf("小丽的口袋里有%d块钱\n", 137);printf("小丽的口袋里有%6.5d块钱\n", 137);printf("小丽的口袋里有%9.6d块钱\n", 137);printf("小丽的口袋里有%7.4d块钱\n", 137);return 0;
}
运行结果如下。

在 %d 一行,数字 137 正常输出。
在 %9.6d 一行,要求精度值为 6,而数字 137 占用 3 个字符宽度,所以呢,数字 137 左边补上了 3 个 0 。最后呢,在精度值为 6 的条件下,宽度字段值为 9,为了凑齐整个的宽度值 9,还需要补上 3 个空格。
在 %7.4d 一行,要求精度值为 4,而数字 137 占用 3 个字符宽度,所以呢,数字 137 左边补上了 1 个 0 。最后呢,在精度值为 4 的条件下,宽度字段值为 7,为了凑齐整个的宽度值 7,还需要补上 3 个空格。
精度值决定了这个数显示为多少个数位,不足精度数的话,就在左边补 0 。宽度字段值,则是说,精度值小于宽度值的话,就补上空格。
到了这里,宽度字段与精度字段,我大体上就算是讲完了。
接下来,我们来讲解各种标志。
五. 符号标志
符号标志,一般来讲,在 %d 格式控制符里面,自动包含了符号标志。也就是说,若某个整数是一个负整数,则会在 %d 输出时,在数字前面加上负号【-】,这就是符号标志的意义。而对于 %c,%x,%X,%s,则不存在符号标志。
%d,用于输出带符号整数。%u,则是用于输出无符号整数。%d,会包含有符号标志,而 %u,则是不含符号标志。因为,无符号数,其实就是自然数,它只包含 0 和 正整数,不包含负整数。
我的建议是,对于符号标志,了解一下就可以了。
六. 正号标志
它的意思是,若是 %d 输出的整数是一个正数或 0,则会在前面加上一个正号。
案例代码如下。
#include <stdio.h>int main(void)
{printf("小丽的口袋里有 %d 块钱\n", 37);printf("小丽的口袋里有 %+d 块钱\n", 37);return 0;
}
运行结果如下。

由于 37 是一个正整数,所以,在 %+d 格式控制符的输出里面,输出的,是【+37】。
七. 左对齐标志
左对齐标志,由负号【-】来标识,需要结合着宽度字段来使用。
默认的情况下,%d,%c,%s 等等的格式控制符所转换出来的东西,在加上了宽度字段以后,空格在左,而有效字符在右边。所以,输出内容默认的对齐方式为右对齐。
然而,若是在与宽度字段连用的时候,加上了左对齐标志【-】,则会让有效的输出字符在左边,补充宽度的空格在右边。
比如,不加左对齐标志的 %6d 所对应的添加了左对齐标志的格式控制符为 %-6d。不加左对齐标志的 %9d 所对应的添加了左对齐标志的格式控制符为 %-9d 。
案例代码如下。
#include <stdio.h>int main(void)
{printf("小丽的口袋里有%d块钱\n", 137);printf("小丽的口袋里有%6d块钱\n", 137);printf("小丽的口袋里有%-6d块钱\n", 137);return 0;
}
运行结果如下。

八. 空格标志
空格标志,由空格来标识。需要与 %d 一类的格式连用。例如,【% d】。
空格标志的意思是,当符号标志开启,也就是使用了 %d 一类的带符号整数的格式控制符的时候,若是整数大于或等于 0,则会在整数前面加上一个空格。
案例代码如下。
#include <stdio.h>int main(void)
{printf("小丽的口袋里有%d块钱\n", 137);printf("小丽的口袋里有% d块钱\n", 137);return 0;
}
运行结果如下。

九. 填零标志
填零标志由数字 0 来标识,一般与宽度字段连用。例如,不加填零标志的 %6d 所对应的添加了填零标志的格式控制符为 %06d 。
案例代码如下。
#include <stdio.h>int main(void)
{printf("小丽的口袋里有%d块钱\n", 137);printf("小丽的口袋里有%06d块钱\n", 137);return 0;
}
运行结果如下。

十. 大小写标志
大小写标志,只对十六进制有意义。使用 %x 的时候,自动添加小写标志。使用 %X 的时候,自动添加大写标志。
当显示 0x30AE 这样的,含有介于字母 A 到 F 之间的某一个十六进制符号的时候,若是使用了 %x,采用了小写标志,则会显示为 0x30ae 。若是使用了 %X,采用了大写标志,则会显示为 0x30AE 。
本条目,我就不给出案例代码了。
十一. 特殊显示标志
特殊显示标志,由井号【#】来标识,只对八进制与十六进制起作用。八进制的简单格式控制符为 %o,十六进制的简单格式控制符为 %x 或 %X 。若是添加了特殊显示标志,比如 %#o,%#x,或 %#X,则在打印输出时,会在八进制数前面加上一个 0,在十六进制数前面加上 0x 或 0X 。
案例代码如下。
#include <stdio.h>int main(void)
{printf("小丽的口袋里有%d块钱\n", 137);printf("小丽的口袋里有%o块钱\n", 137);printf("小丽的口袋里有%#o块钱\n", 137);printf("小丽的口袋里有%x块钱\n", 137);printf("小丽的口袋里有%#x块钱\n", 137);printf("小丽的口袋里有%X块钱\n", 137);printf("小丽的口袋里有%#X块钱\n", 137);return 0;
}
运行结果如下。

结束语
本节的内容很多,但是呢,难度应该是不大的。
关于格式控制符的内容,你还可以自己多去进行一些个尝试的。
总算是讲完了 Windows 编程的预备知识了。
预备知识的讲解,对我来讲,也是有些累的。
所以呢,接下来,我可能会休息几天。跟着本专栏学习的同学,可能需要略作等待了。
在我写作的当前,我暂时先不打算去讲解 Win32 知识,而是先去讲解 MFC 的知识。至于 Win32 知识什么时候讲,再说了。
反正,此刻,我是打算先去讲解 MFC 的。
下一节,我们就开始正式的 Windows 编程章节的讲解了。
专栏导航
本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏,故划分为两个专栏导航。读者可以自行选择前往哪个专栏。
(一)WIn32 专栏导航
上一篇:编程技能:格式化打印04,sprintf
回到目录
下一篇:无
(二)MFC 专栏导航
上一篇:编程技能:格式化打印04,sprintf
回到目录
下一篇:无