你真的懂C的格式化I/O吗?(上)

前言

说起C语言I/O操作,学过C的人第一反应想到的肯定是printf/scanf之类的函数,那可是C语言的基础,程序调试的必备技能 😃。不错,作为一名C程序猿,printf确实是使用最为高频的函数了。但是如果我要问,如何格式化输入/输出我们想要的内容,大家可能就有点心虚了。为了修补上这个bug,今天我就和大家一起学习一下C的格式化I/O。

格式化输出

下面是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或Lh表示将整数作为short类型打印,字母l表示将整数作为long类型打印,字母L表示将整数作为long double类型输出

下面是转换字符部分的定义:

字符参数类型;输出形式
d,iint类型;十进制
oint类型;无符号八进制书(没有前导0)
x,Xint类型;无符号十六进制(没有前导0x或0X),10-15分别用abcdef或ABCDEF表示
uint类型;无符号十进制数
cint类型;单个字符
schar *类型;顺序打印字符串中的字符,直到遇到’\0’或已打印了由精度指定的字符数为止
fdouble类型;十进制小数[-]m.dddddd,d的个数由精度控制(默认值为6)
e,Edouble类型;[-].m.dddddde±xx或[-]m.ddddddE±xx,d的个数由精度控制(默认值为6)
g,Gdouble类型;
pvoid *类型;指针(取决于具体实现)
%不转换参数;打印一个百分号%

除了e,E和g,G一般不怎么经常使用之外,其他的字符使用频率还是很高的。

下面通过几个例子,展示一下上面转换字符和控制字符的具体用法。

  1. 示例一:以十六进制打一个整数,其他进制的整数输出类似

普通打印:

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
  1. 示例二: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语言的基本数据类型可以参考这里

  1. 示例三:字符串输出
    这里说一下,字符串的精度问题,下表分别按照不同的格式打印“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     :
  1. 示例四:小数输出
    小数的输出主要是精度的问题,精度默认为6位。
float f = 1.0f/3;
printf("%.10f\n", f);
输出:0.3333333433
  1. 示例五:指针输出
int i  = 10;
printf("%p\n", &i);
输出:0x7ffcf9e4f270
  1. 示例六:转换说明中,宽度和精度可以用*号代替

如果宽度和精度用*代替的话,宽度和精度的值通过转换下一个参数(必须为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致敬!


更多优质内容,请移步公众号奔跑的码仔

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值