格式化输出
下面是ANSI C定义的标准的输出函数族:
#include <stdio.h> printf(char *format, ...); vprintf(const char *format, va_list arg); sprintf(char *s, const char *format, ...); vsprintf(char *s, const char *format, va_list arg); fprintf(FILE *stream, const char *format, ...); vfprintf(FILE *stream, const *format, va_list arg); ... ...
可以看到上面的函数的区别是(a:输出的目的地不同;(b:输出内容的格式不同。但是每个函数都会带有一个format参数,这个参数就是用于格式化最终输出内容的。下面以printf为例讲解一下如何通过format控制其各个参数的格式化。
printf函数通过format中定义的格式化项目,控制其对应的参数进行格式化,并将格式化之后的内容输出到标准输出设备上,其返回值为打印的字符数。
format参数包括两种类型的对象:普通字符和转换说明。在输出时,普通字符将原样输出,转换说明用于控制printf中的参数的格式化。每个转换说明都是%开始,并一个转换字符结束,比如,大家熟悉的%d,%s等,其中d和s就是转换字符。可是在% 和转换字符之间其实可以加入很多控制字符,以达到精细地控制输出格式。
下面是这些控制字符部分的定义:
控制部分:[-|+|空格|0|#][num][.][num][h|l|L]
字符 | 含义 |
- | 指定被转换的参数按照左对齐的形式输出(默认是右对齐) |
+ | 指定在输出的数前面加上正负号 |
空格 | 如果第一个字符不是正负号,则在其前面加上一个空格 |
0 | 对于数值转换,当输出长度小于段宽度时,添加前导0进行填充 |
# | 指定另一种输出形式。如果为o(八进制)转换,则第一个数字为0。如果为x或X,则在非0值前,加0x或0X |
数字 | 指定最小字段宽度。转换后的参数将打印不小于最小字段宽度的字段,不足的位置用空格或0填充 |
小数点 | 用于将字段宽度和精度分开 |
数字 | 用于指定精度,即指定字符串中要打印的最大字符数,浮点数小数点后的位数、整型最少输出的数字数目 |
h或l或L | h表示将整数作为short类型打印,字母l表示将整数作为long类型打印,字母L表示将整数作为long double类型输出 |
下面是转换字符部分的定义:
字符 | 参数类型;输出形式 |
d,i | int类型;十进制 |
o | int类型;无符号八进制书(没有前导0) |
x,X | int类型;无符号十六进制(没有前导0x或0X),10-15分别用abcdef或ABCDEF表示 |
u | int类型;无符号十进制数 |
c | int类型;单个字符 |
s | char *类型;顺序打印字符串中的字符,直到遇到'\0'或已打印了由精度指定的字符数为止 |
f | double类型;十进制小数[-]m.dddddd,d的个数由精度控制(默认值为6) |
e,E | double类型;[-].m.dddddde±xx或[-]m.ddddddE±xx,d的个数由精度控制(默认值为6) |
g,G | double类型; |
p | void *类型;指针(取决于具体实现) |
% | 不转换参数;打印一个百分号% |
除了e,E和g,G一般不怎么经常使用之外,其他的字符使用频率还是很高的。 |
下面通过几个例子,展示一下上面转换字符和控制字符的具体用法。
- 示例一:以十六进制打一个整数,其他进制的整数输出类似
普通打印:
printf("0x%x\n", 1048576); 输出:0x100000
如果该整数是一个内存地址,CPU为32位,打印格式形如:0x00000001
printf("0x%.8x\n", 1048576);//.8表示整数的最小输出的数字数目为8 或者 printf("0x%08x\n", 1048576);//注意:08表示不足的位置用0填充,以保证8个字符的最小宽度。 输出:0x00100000
- 示例二:h或l的使用
假设系统为32bit的,int:4字节,long:4字节,long long:8字节
printf("%d\n", 4294967295U);//注意U不能省略,否则按照long型处理。4294967295:0xFFFF FFFF 输出:-1 printf("%u\n", 4294967295U); 输出:4294967295 printf("%lu\n", 4294967295UL); 输出:4294967295 printf("%lld\n", 4294967295L); 输出:4294967295 printf("%hu\n", 4294967295L);//h将4294967295L截断为65535 输出:65535
关于C语言的基本数据类型可以参考这里。
- 示例三:字符串输出
这里说一下,字符串的精度问题,下表分别按照不同的格式打印“Hello, world”(12字符)。
printf(":%s:\n", "Hello, world"); | :Hello, world: printf(":%10s:\n", "Hello, world"); | :Hello, world: printf(":%.10s:\n", "Hello, world"); | :Hello, wor: printf(":%.15s:\n", "Hello, world"); | :Hello, world: printf(":%15s:\n", "Hello, world"); | : Hello, world: printf(":%-15s:\n", "Hello, world"); | :Hello, world : printf(":%15.10s:\n", "Hello, world"); | : Hello, wor: printf(":%-15.10s:\n", "Hello, world"); | :Hello, wor :
- 示例四:小数输出
小数的输出主要是精度的问题,精度默认为6位。
float f = 1.0f/3; printf("%.10f\n", f); 输出:0.3333333433
- 示例五:指针输出
int i = 10; printf("%p\n", &i); 输出:0x7ffcf9e4f270
- 示例六:转换说明中,宽度和精度可以用*号代替
如果宽度和精度用*代替的话,宽度和精度的值通过转换下一个参数(必须为int)来计算,比如,为了从字符串s中打印最多max个字符,可以使用下列语句:
printf("%.*s", max, s);//字符串s的打印精度由参数max确定
7.示例七:函数printf使用第一个参数判断后面参数的个数及其类型。如果参数的个数不够或者类型错误,则将得到错误的结果。注意下面两个函数调用的输出之间的区别。
printf("%shello,world!\n");//如果打印的字符串中有字符%,输出将会出错。 printf("%s\n", "%shello,world!"); 输出: hello,world! %shello,world!
其他的输出函数,比如sprintf、fprintf等,同printf函数一样,都是按照format格式化后面的各个参数,所不同的是结果的输出位置不同,sprintf将其存在一个字符数组中,fprintf将其输出到特定的输出流中。
未完待续... ...
参考资料:《C程序设计语言》,一本每个C程序员都要精读的书,向伟大的祖师爷 Brian W.Kernighan & Dennis M.Ritchie致敬!