目录
1 格式化I/O简介
在先前示例代码中,经常使用库函数 printf()
来输出程序中的打印信息,printf()
函数能够将格式化的数据输出到标准输出设备,因此它通常被称为格式化输出函数。除了 printf()
,还提供了其他几种格式化输出函数,包括 fprintf()
、dprintf()
、sprintf()
和 snprintf()
。
与格式化输出相对应的是格式化输入,它允许从标准输入中获取格式化数据。格式化输入函数包括 scanf()
、fscanf()
和 sscanf()
。
2 格式化输出
2.1 格式化输出函数简介
C 库函数提供了 5 个格式化输出函数,包括: printf()、 fprintf()、 dprintf()、 sprintf()、 snprintf(),其函数定义如下所示:
printf():
将格式化的数据输出到标准输出(通常是终端或控制台)。
int printf(const char *format, ...);
fprintf():
将格式化的数据输出到指定的文件,stream
参数可以是任何有效的文件指针。
int fprintf(FILE *stream, const char *format, ...);
dprintf():
直接将格式化的数据写入到指定的文件描述符 fd
。
int dprintf(int fd, const char *format, ...);
sprintf():
将格式化的数据写入到字符数组 str
中,这通常用于字符串操作。
int sprintf(char *str, const char *format, ...);
snprintf():
类似于 sprintf()
,但增加了对输出缓冲区大小的限制,size
参数指定了最大可写入的字符数,包括结尾的空字符。
int snprintf(char *str, size_t size, const char *format, ...);
2.2 格式控制字符串 format
格式化输出函数中的format 参数称为格式控制字符串,顾名思义,首先它是一个字符串的形式,其次它能够控制后续变参的格式转换。格式控制字符串由两部分组成:普通字符(非%字符) 和转换说明。普通字符会进行原样输出,每个转换说明都会对应后续的一个参数,通常有几个转换说明就需要提供几个参数, 使之一一对应。如下所示:
printf("转换说明 1 转换说明 2 转换说明 3", arg1, arg2, arg3);
格式控制字符串(format )定义了输出数据的格式,包括数据类型、宽度、精度、填充字符等。格式控制字符串通常包括以下几个部分:
转换说明符(Conversion Specifier):指定要输出的数据类型,例如:
%d
表示十进制整数。%f
表示浮点数。%s
表示字符串。%c
表示字符。%x
或%X
表示十六进制整数(小写或大写)。
标志(Flags):可以改变输出格式的选项,例如:
%-
表示左对齐。%+
表示总是显示正负号。%0
表示使用零填充到指定宽度。%#
表示显示八进制数的前缀0
或十六进制数的前缀0x
或0X
。
宽度(Width):指定输出的最小字符数,如果数据不足,会用空格或指定的填充字符填充。
精度(Precision):对于浮点数,指定小数点后的位数;对于字符串,指定最大字符数。
长度修饰符(Length Modifier):指定数据的长度,例如:
%l
表示长整型(long int
)。%h
表示短整型(short int
)。%ll
表示长长整型(long long int
)。
填充字符:如果指定了宽度,可以使用填充字符来填充输出,例如 %10s
会输出一个字符串,并且在其前后填充空格直到宽度达到10个字符。
每个转换说明都是以%字符开头,只有%和 type 字段是必须的,其余都是可选的。其格式如下所示(使用[ ]括起来的部分是可选的) :
%[flags][width][.precision][length]type
例如下面的示例输出:
printf("%d\n", 123);
//输出:123
//注释:以十进制形式输出整数123,后面跟着一个换行符。
printf("%o\n", 123);
//输出:173
//注释:将整数123转换为八进制形式并输出,后面跟着一个换行符。在八进制中,123等于173。
printf("%06d", 1000);
//输出:001000
//注释:以零填充到至少6位的宽度输出整数1000。由于1000不足6位,前面用0填充。
printf("%.8f\n", 520.1314);
//输出:520.13140000
//注释:以浮点数形式输出520.1314,精度指定为8位小数,因此输出时会显示8位小数,即使最后几位是0。
printf("%lld\n", 12345);
//输出:12345
//注释:将整数12345以long long int类型输出,lld是长度修饰符,表示长长整型(64位)。即使没有这个修饰符,大多数现代编译器也会将整型默认为int类型,但使用lld可以确保在所有编译器中都以长整型处理。
2.3 示例程序
下面的程序展示了 printf()
、fprintf()
、dprintf()
、sprintf()
和 snprintf()
函数的使用。每个函数都用来输出格式化的字符串,但它们输出到的地方不同。
#include <stdio.h>
int main() {
int number = 10;
char str[] = "Hello, World!";
float pi = 3.14159;
// 使用 printf() 输出到标准输出
printf("Standard output: %s\n", str);
// 使用 fprintf() 输出到文件
FILE *file = fopen("output.txt", "w");
if (file != NULL) {
fprintf(file, "File output: %s\n", str);
fclose(file);
}
// 使用 dprintf() 输出到文件描述符
dprintf(fileno(stdout), "Standard output descriptor: %d\n", number);
// 使用 sprintf() 将格式化字符串存储到字符数组
char buffer[100];
sprintf(buffer, "Buffer output: %.2f\n", pi);
printf("%s", buffer);
// 使用 snprintf() 将格式化字符串存储到字符数组,限制长度
snprintf(buffer, sizeof(buffer), "Buffer output with limit: %.2f\n", pi);
printf("%s", buffer);
return 0;
}
printf()
直接将格式化的字符串输出到标准输出(通常是控制台)。fprintf()
需要一个文件指针作为第一个参数,然后将格式化的字符串写入到该文件。dprintf()
类似于fprintf()
,但它使用文件描述符而不是文件指针。sprintf()
将格式化的字符串存储到提供的字符数组中。需要确保数组足够大以避免溢出。snprintf()
与sprintf()
类似,但提供了一个额外的长度参数来限制写入的字符数,这有助于防止溢出。
程序运行的结果如下:
3 格式化输入
3.1 格式化输入简介
C 库函数提供了 3 个格式化输入函数,包括: scanf()、 fscanf()、 sscanf(),下面是每个函数的简要介绍:
scanf()
:从标准输入(通常是键盘输入)读取格式化输入。
int scanf(const char *format, ...);
fscanf()
:从指定的文件中读取格式化输入,文件通过 FILE 指针指定,它有两个固定参数, FILE 指针和格式控制字符串 format。
int fscanf(FILE *stream, const char *format, ...);
sscanf()
:从字符串中读取格式化输入。
int sscanf(const char *str, const char *format, ...);
3.2 格式控制字符串 format
与格式化输出函数中的 format 参数格式、写法上比较相似,但也有一些区别。 format 字符串包含一个或多个转换说明,每一个转换说明都是以百分号"%"或者"%n$"开头(n 是一个十进制数字),关于"%n$"这种开头的转换说明使用的不多。
转换格式与输入类似,以%百分号开头的转换说明一般格式如下:
%[*][width][length]type
%[m][width][length]type
%*不会对转换后的结果进行存储:后面可选择性添加星号*或字母 m,如果添加了星号*,格式化输入函数会按照转换说明的指示读取输入,但是丢弃输入,意味着不需要对转换后的结果进行存储,所以也就不需要提供相应的指针参数。
%m会对转换后的结果进行存储:如果添加了 m,它只能与%s、 %c 以及%[一起使用,调用者无需分配相应的缓冲区来保存格式转换后的数据,原因在于添加了 m,这些格式化输入函数内部会自动分配足够大小的缓冲区,并将缓冲区的地址值通过与该格式转换相对应的指针参数返回出来,该指针参数应该是指向 char *变量的指针。随后,当不再需要此缓冲区时,调用者应调用 free()函数来释放此缓冲区。
3.3 示例程序
下面的程序演示了标准输入输出函数 scanf()
、fprintf()
、fscanf()
和 sscanf()
的基本用法。
#include <stdio.h>
int main() {
int i;
float f;
char s[50];
FILE *file;
// 使用 scanf() 从标准输入读取整数和浮点数
printf("Enter an integer and a float: ");
scanf("%d %f", &i, &f);
printf("You entered: %d, %.2f\n", i, f);
// 写入数据到 input.txt 文件
file = fopen("input.txt", "w");
if (file == NULL) {
printf("Failed to open file for writing.\n");
} else {
fprintf(file, "fscanf test!\n");
fclose(file);
}
// 读取 input.txt 文件中的数据
file = fopen("input.txt", "r");
if (file == NULL) {
printf("Failed to open file for reading.\n");
} else {
fscanf(file, "%8s ", s);
printf("Read from file: %s\n", s);
fclose(file);
}
// 使用 sscanf() 从字符串读取数据
sscanf("123 456.78 Hello", "%d %f %s", &i, &f, s);
printf("Parsed: %d, %.2f, %s\n", i, f, s);
return 0;
}
代码首先通过 scanf()
从用户那里获取一个整数和一个浮点数,并打印出来。然后写入文本到 input.txt
文件,并通过f
scanf()
读取这个文件的前8个字符,打印读取的内容。最后通过 sscanf()
从固定字符串中解析数据,并将结果输出。程序运行结果如下: