c#发展

首页 » 常识 » 常识 » C语言输入输出流16scanf函数的格式
TUhjnbcbe - 2024/12/31 19:05:00
北京现在治疗白癜风大概多少钱 http://baidianfeng.39.net/index.html

在文章里,我们详细介绍了标准输入流stdin及其库函数getchar、gets、gets_s,以及针对gets和gets_s函数的不足,建议使用fgets函数的原因和用法。

gets函数、gets_s函数、fgets函数都属于无格式化的输入函数,也就是说输入什么就读取什么,而且只能读取char类型字符串,无法读取其他类型数据,无法对输入的字符进行控制。

从本篇开始我们介绍stdin的另一种函数:格式化输入可函数。这些库函数可以对输入的字符进行各种控制(解析),比如可以输入不同类型的数据,也可以控制输入数据的宽度,对应的库函数是scanf和它的安全版本scanf_s,以及scanf函数的可变参版本vscanf以及对应的安全版本vscanf_s。

我们先从scanf函数详细讲起。

scanf函数

这个函数是C语言格式化输入函数的最重要的一个函数,今天我们详细的学习一下。

函数原型:

intscanf(constchar*format,...);

参数format是一个字符串,这个字符串里的不同字符按照特定的规则结合在一起,可以实现各种“格式控制功能”,用来对键盘上输入的字符按照这些“格式控制指令”的要求进行解析后再读取到内存中。

听起来好像比较复杂,其实很简单。比如:

scanf(“%d”,d);

字符串”%d”中的%d就是一个格式控制指令,其中%表示后面紧跟的某些特定字母(不是字符)是类型说明符,比如此处%后面紧跟的字母是d,就表示十进制整数(decimal),%和d共同构成一个变量占位符“%d”,表示后面的参数对应的变量必须是整型变量,该参数就是指向变量d的指针,此处就是参数d。

注意,%d中的d就是类型说明符,表示只能接收键盘上输入十进制整数,不接受其他进制整数的输入。d中的d是整型变量名。

d的d是一个整型变量,d表示传入scanf函数的是变量d的地址。格式字符串里有几个变量占位符,就对应后面有几个对应类型的变量地址。

下面是一个简单的例子:

intd;

charc;

floatf;

scanf("%d%c%f",d,c,f);

printf("%d,%c,%f\n",d,c,f);

完整代码截图如下:

图1

程序的运行效果如下图:

图2

上面的演示程序里,scanf的格式字符串里有个变量占位符,分别是int型、char型号、float型变量,变量占位符和后面的参数类型是按照先后顺序一一对应的。

键盘输入的数据如何分隔?

变量站位符中间有空格作为分隔符,键盘输入对应数据时就必须要同样输入对应的分隔符。比如中间如果用1个逗号分隔:

scanf("%d,%c,%f",d,c,f);

输入的时候就必须也只能输入1个逗号,不能多输入。其他分隔符的输入数量也必须严格遵守,不过空格分隔符是个例外,无论在格式字符串里输入多少个空格,在键盘上输入空格时,数量不需要一致。

也可以用换行符作为分隔符。比如:

scanf("%d\n%c\n%f",d,c,f);

注意!换行符,以及制表符(TAB键或’\t’)都被作为空白字符对待!scanf函数在扫描时如果没有空白符,也不会解析错误,会忽略,但如果换成其他分隔符,比如逗号等,就容易产生解析错误。

scanf和printf格式字符串的相似和不同

当我们输入小数(实数)时,我们明明输入了2位小数,但是系统却没有原样输出,而是给出了6位,那是因为float型的十进制小数位数默认就是6位。

如果希望能原样输出,就可以在printf函数中的%f中间插入%0.2f,或者%后面直接是小数点,省略数字0,即%.2f,表示输出的小数无论存储的是小数点后多少位,输出的时候只显示小数点后2位。

键盘输入了大于2位小数,比如.,输出的时候也只能是.14。

但是,scanf函数中的float型变量占位符中间不能插入小数点后位数的宽度限制,只能在printf函数使用!

通过刚才的这个例子,我们会发现,printf函数和scanf的用法很相似,特别是在格式字符串方面,但是还是有一些细微差别的,比如可变参数列表里的变量就不需要参数的实际地址,只需要参数名即可。后面我们会有文章来详细讲解和对照分析。

如何实现数据类型的不同输入形式?

整数的不同输入形式

如果我们需要输入一个整型数据,对应的类型说明符d(十进制:decimal),表示键盘上只能以十进制的形式输入。如果输入一个十六进制数据,scanf函数通过%d就无法识别。

类型说明符o(八进制:octal)表示接收键盘输入的八进制整数,类型说明符x(十六进制:hexadecimal)表示接收键盘输入十六进制数。类型说明符u(无符号的:unsigned)表示从键盘上接收无符号的整数(正整数)。

scanf函数还提供了%i,这样我们在键盘上可以输入八进制、十进制、十六进制都可以。如果输入八进制,必须要以数字0开头,要输入十六进制,要以0x(数字0和x)开头。

我们通过一个例子来熟悉下:

scanf("%d",decimal);

scanf("%o",octal);

scanf("%x",hex);

scanf("%u",unsigned_num);

scanf("%i",i);

在vs环境下的完整代码截图如下

程序运行结果如下:

图4

当%o和%x时,直接输入对应进制的数值即可,当为%i时,输入8进制和16进制时,需要注意输入数字的前缀。如果前缀是0,表示后面必须输入八进制,但如果错误输入,比如输入8,即08,程序就异常结束。如果前缀是0x,后面输入的就是16进制的数字。

单精度和双精度浮点数的类型说明符

C语言中浮点数分为单精度浮点数和双精度浮点数,单精度浮点数小数点后最多为6位,用float表示,双精度浮点数小数点后为15位,用double表示。

float型变量的类型说明符为f,%f表示键盘上最多只能输入6位小数。double型变量采用%lf表示,lf是longfloat的缩写,键盘上可以输入最多15位小数。

浮点数的科学计数法输入形式

浮点数也可以以科学计数法的形式输入。比如41.26,用科学计数法表示就是.*10^2,在键盘输入时,10用字母e或E表示,写成.e2即可。

对应的scanf的类型说明符为e(指数exponential),比如:

floate;

scanf(“%e”,e);

程序运行后,键盘上可以输入.e2即可。注意,如果输入的浮点数是double类型,可以用%le表示。

相似数据类型的大小前缀修饰符

double型修饰符:%lf、%le

在上面我们多次提到单精度浮点数的类型说明符是f,如果需要输入double类型,就用字母l修饰,%lf中l就是大小修饰符,%e也可以用l修饰,%le表示double类型数据的科学计数法形式。

longdouble型修饰符:%Lf、%Le

如果是longdouble型数据,就需要大写字母L修饰:%Lf、%Le。

long型修饰符(长整型:longint)

如果需要输入长整型变量,那么所有整型变量的修饰符前都可以直接用字母l修饰。比如:%ld、%lo、%lx、%lu、%li。

longlong型修饰符

用两个字母l修饰即可,如%lld、%llo、%llx、%lli。

short型修饰符号(短整型:shortint)

用字母h修饰,如%hd、%ho、%hx、%hu、%hi。

字符串的输入和宽度限制

scanf函数可以接收字符串的输入,对应的类型说明符是字符串string的s,因为C语言没有“字符串”(string)这种基本数据类型,所以一般都是用字符数组(或malloc分配的堆内存来接收,不懂也没关系)来存储。

一般不能直接用%s,比如面的写法是错误的:

charstr[5];

scanf(“%s”,str);

如果持续看我文章的读者,应该知道怎么回事。因为我在之前的文章里多次说到,字符串的输入一定要明确的规定其输入的字符个数(长度),不然很容易会因为输入的字符个数超过接收的数组的长度,而造成缓冲区溢出的危险。

既然字符数组str的长度是5,所以我们就需要通过格式控制指令来告诉scanf函数,键盘上接收的当前字符串长度只能不能大于5个字符,否则就会造成数组越界。

但是由于明确知道是字符串,所以scanf函数会自动在接受键盘上输入的字符串(严格的说,此时还不能叫字符串,应该是多个字符)之后填上’\0’空字符,这时候才是真正合格的字符串,然后复制到str指向的区域。

因此,考虑到scanf函数要在字符末尾添加空字符,所以实际上键盘上最大只能接收4个字符。当然,也可以接受1到个字符。

所以,在%s中间插入要限制的字符个数,只能是1到4。比如是%1s表示键盘输入的字符串的个数只能是1个字符,实际上存入str区域的是2个字节。str[0]是键盘上接收的有效字符,str[1]’\0’。

以此类推,%2s、%s、%4s分别表示str[2]、str[]、str[4]存储的都是空字符。

我们一般把%s中间的数字称为宽度限制字段。

宽度修饰符

宽度修饰符也可以用来修饰字符、整数、浮点数的整数部分等。如下:

chars1[],s2[],s[];

scanf(“%2s%s%c”,s1,s2,s);

%2s表示只从键盘接收2个有效字符并且自动加上第三个空字符,构成一个三字节的字符串。假设键盘上输入字母a和b,那么:

s1[0]=‘a’;

s1[1]=‘b’;

s2[2]=‘\0’;

%s会让数组s2下标越界,是错误的,会造成程序可能异常退出。

%c表示从键盘上连续接收个有效字符,如果键盘上输入’w’、’t’、’o’,那么:

s2[0]=‘w’;

s2[1]=‘t’;

s22[2]=‘o’;

不会像字符串一样在末尾自动添加空字符’\0’。一般情况下,如果我们需要获得一个字符串,就会用类型说明s,如果我们需要获得多个字符,就会用类型说明符c和宽度限制符结合起来使用。

宽度修饰符也经常被用在对整数的宽度限制上。比如:

intn1,n2;

scanf(“%d%1d”,n1,n2);

如果键盘上有如下三组输入:

2

我们来分析下,首先第一行输入连续的一组数字是12,正好符合第一个变量的格式要求:1到位整数,因此n1=12。然后空格进行分割,紧接着的数字1也正好满足第二个变量的格式要求:1位整数,因此n2=1。

第二行的第一组数字是12,符合变量n1的宽度要求,%d表示1到位整数。第二组数字12会从左往右只截取数字1赋值给n2,因为n2的最大宽度是1。

第三行的第一组数字是,n1的最大宽度为,因此n1=12没问题,因为n2和n1本来需要有空格分隔,但没有输入空格,直接是数字4,注意!!!没有空格时,scanf函数直接忽略,将数字4赋值给n2,然后停止解析。

注意!如果用其他符号,比如逗号作为分隔符,scanf函数就不会忽略,解析就会出错。比如:

intn1,n2;

scanf(“%d,%1d”,n1,n2);

输入一串数字:

,1

n1仍然是12,但是紧随其后的不是逗号,是数字4,因此scanf函数的解析是错误的,只有空格没有时可以被忽略,其他的分隔符在输入时必须保持一致,因此终止解析。

%c和%1c、%d和%1d的宽度区别

%c和%1c是相同的含义,表示读取1个字符。%d和%1d(是数字1,不是字母l)是完全不同的含义。%1d表示最多只能读取1位数字,%d表示读取的数字位数没有限制。

只匹配、不存储的通配符:*

百分号后面的星号(*)将取消下一个输入字段的分配(将被解释为指定类型的字段)。将扫描该字段但不将其存储在参数中。

举个例子:

intn1,n2;

scanf(“%d%*2d%d”,n1,n2);

因为格式字符串有个变量占位符,因此键盘上必须输入个整型变量,并且第二个变量占位符明确指定了该整型变量的宽度是2,因此下面是一个合理的输入:

第一个整数12和第三个整数将被存储到n和n2里,4将作为校验码只被scanf函数检查输入是否匹配格式指令,而不被存储到任何变量。

总结

今天介绍了scanf函数的格式字符串中很多重要的格式控制码的用法,这些都是非常重要、基础的用法。下一章我们将介绍scanf函数的格式控制字符串中的最后一个用法:正则表达式。

段誉,年6月20日,写于合肥。

1
查看完整版本: C语言输入输出流16scanf函数的格式