C 库函数提供了 3 个格式化输入函数,包括:scanf()、fscanf()、sscanf(),其函数定义如下所示:
#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
可以看到,这 3 个格式化输入函数也是可变参函数,它们都有一个共同的参数 format,同样也称为格式控制字符串,用于指定输入数据如何进行格式转换,与格式化输出函数中的 format 参数格式相似,但也有所不同。
每个函数除了固定参数之外,还可携带 0 个或多个可变参数。
scanf()函数可将用户输入(标准输入)的数据进行格式化转换;fscanf()函数从 FILE 指针指定文件中读取数据,并将数据进行格式化转换;sscanf()函数从参数 str 所指向的字符串中读取数据,并将数据进行格式化转换。
scanf()函数
相对于 printf 函数,scanf 函数就简单得多。scanf()函数的功能与 printf()函数正好相反,执行格式化输入功能;即 scanf()函数将用户输入(标准输入)的数据进行格式化转换并进行存储,它从格式化控制字符串format 参数的最左端开始,每遇到一个转换说明便将其与下一个输入数据进行“匹配”,如果二者匹配则继续,否则结束对后面输入的处理。而每遇到一个转换说明,便按该转换说明所描述的格式对其后的输入数据进行转换,然后将转换得到的数据存储于与其对应的输入地址中。以此类推,直到对整个输入数据的处理结束为止。
从函数原型可以看出,scanf()函数也是一个“可变参数函数”,除第一个参数 format 之外,scanf()函数还可以有若干个输入地址(指针),这些指针指向对应的缓冲区,用于存储格式化转换后的数据;且对于每一个输入地址,在格式控制字符串 format 参数中都必须有一个转换说明与之一一对应。即从 format 字符串的左端第 1 个转换说明对应第 1 个输入地址,第 2 个格式说明符对应第 2 个输入地址,第 3 个格式说明符对应第 3 个输入地址,以此类推。譬如:
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
当程序中调用 scanf()的时候,终端会被阻塞,等待用户输入数据,此时我们可以通过键盘输入一些字符,譬如数字、字母或者其它字符,输入完成按回车即可!接着来 scanf()函数就会对用户输入的数据进行格式转换处理。
函数调用成功后,将返回成功匹配和分配的输入项的数量;如果较早匹配失败,则该数目可能小于所提供的数目,甚至为零。发生错误则返回负值。
fscanf()函数
fscanf()函数从指定文件中读取数据,作为格式转换的输入数据,文件通过 FILE 指针指定,所以它有两个固定参数,FILE 指针和格式控制字符串 format。譬如从标准输入文件中读取数据进行格式化转换:
int a, b, c;
fscanf(stdin, "%d %d %d", &a, &b, &c);
此时它的作用与 scanf()就是相同的,因为标准输入文件的数据就是用户输入的数据,譬如通过键盘输入的数据。
函数调用成功后,将返回成功匹配和分配的输入项的数量;如果较早匹配失败,则该数目可能小于所提供的数目,甚至为零。发生错误则返回负值。
sscanf()函数
sscanf()将从参数 str 所指向的字符串缓冲区中读取数据,作为格式转换的输入数据,所以它也有两个固定参数,字符串 str 和格式控制字符串 format,譬如:
char *str = "5454 hello";
char buf[10];
int a;
sscanf(str, "%d %s", &a, buf);
函数调用成功后,将返回成功匹配和分配的输入项的数量;如果较早匹配失败,则该数目可能小于所提供的数目,甚至为零。发生错误则返回负值。
格式控制字符串 format
本小节的重点依然是这个 format 参数的格式,与格式化输出函数中的 format 参数格式、写法上比较相似,但也有一些区别。format 字符串包含一个或多个转换说明,每一个转换说明都是以百分号"%"或者"%n$"
开头(n 是一个十进制数字),关于"%n$"这种开头的转换说明就不介绍了,实际上用的不多。
以%百分号开头的转换说明一般格式如下:
%[*][width][length]type
%[m][width][length]type
%后面可选择性添加星号或字母 m,如果添加了星号,格式化输入函数会按照转换说明的指示读取输入,但是丢弃输入,意味着不需要对转换后的结果进行存储,所以也就不需要提供相应的指针参数。
如果添加了 m,它只能与%s、%c 以及%[一起使用,调用者无需分配相应的缓冲区来保存格式转换后的数据,原因在于添加了 m,这些格式化输入函数内部会自动分配足够大小的缓冲区,并将缓冲区的地址值通过与该格式转换相对应的指针参数返回出来,该指针参数应该是指向 char *变量的指针。随后,当不再需要此缓冲区时,调用者应调用 free()函数来释放此缓冲区。譬如:
char *buf;
scanf("%ms", &buf);
......
free(buf);
介绍了星号*和字母 m 之后,再来看看转换说明的格式,中括号[ ]表示的部分是可选的,所以可知,与格式化输出函数中的 format 参数一样,只有 type 字段是必须的。
⚫ width:最大字符宽度;
⚫ length:长度修饰符,与格式化输出函数的 format 参数中的 length 字段意义相同。
⚫ type:指定输入数据的类型。
我们先来看看 type 字段。
(一)type(类型)
此 type 字段与格式化输出函数中的 format 参数的 type 字段是同样的意义,用于指定输入数据的类型,
如下所示:
type 类型描述
(二)width 最大字符宽度
是一个十进制表示的整数,用于指定最大字符宽度,当达到此最大值或发现不匹配的字符时(以先发生者为准),字符的读取将停止。大多数 type 类型会丢弃初始的空白字符,并且这些丢弃的字符不会计入最大字符宽度。对于字符串转换来说,scanf()会在字符串末尾自动添加终止符"\0",最大字符宽度中不包括此终止符。
譬如调用 scanf()函数如下:
scanf("%4s", buf); //匹配字符串,字符串长度不超过 4 个字符
用户输入 abcdefg,按回车,那么只能将 adcd 作为一个字符串存储在 buf 数组中。
(三)length 长度修饰符
与格式化输出函数的格式控制字符串 format 中的 length 字段意义相同,用于对 type 字段进行修饰,扩展识别更多不同长度的数据类型。如下所示:
length 长度修饰符
scanf("%hd", var);//匹配 short int 类型数据
scanf("%hhd", var);//匹配 signed char 类型数据
scanf("%ld", var);//匹配 long int 类型数据
scanf("%f", var);//匹配 float 类型数据
scanf("%lf", var);//匹配 double 类型数据
scanf("%Lf", var);//匹配 long double 类型数据
关于格式化输入函数的 format 参数就介绍到到这里了,接下来编写一个简单地示例进行测试。
使用示例
//示例代码 4.8.2 scanf()函数使用示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int a;
float b;
char *str;
printf("请输入一个整数:\n");
scanf("%d", &a);
printf("你输入的整数为: %d\n", a);
printf("请输入一个浮点数:\n");
scanf("%f", &b);
printf("你输入的浮点数为: %f\n", b);
printf("请输入一个字符串:\n");
scanf("%ms", &str);
printf("你输入的字符串为: %s\n", str);
free(str); //释放字符串占用的内存空间
exit(0);
}
当程序中调用 scanf()之后,终端就会被阻塞、等待用户输入数据,当我们输入完成之后,按回车即可!
第三个 scanf()函数调用中,使用%m,所以我们不需要提供存放字符串的缓冲区,scanf()函数内部会分配缓冲区,并将缓冲区地址存放在 str 这个我们给定的 char 指针变量中。使用完之后记得调用 free()释放内存即可。
编译测试:
测试结果