(来源 C Primer Plus page221)
一、从scanf()角度看输入
接下来,我们更详细地研究scanf()怎样读取输入。假设scanf()根据一个%d转换说明读取一个整数。scanf()函数每次读取一个字符,跳过所有的空白字符,直至遇到第1个非空白字符才开始读取。因为要读取整数,所以scanf()希望发现一个数字字符或者一个符号(+或-)。如果找到一个数字或符号,它便保存该字符,并读取下一个字符。如果下一个字符是数字,它便保存该数字并读取下一个字符。scanf()不断地读取和保存字符,直至遇到非数字字符。如果遇到一个非数字字符,它便认为读到了整数的末尾。然后,scanf()把非数字字符放回输入。这意味着程序在下一次读取输入时,首先读到的是上一次读取丢弃的非数字字符。最后,scanf()计算已读取数字(可能还有符号)相应的数值,并将计算后的值放入指定的变量中。
如果使用字段宽度,scanf()会在字段结尾或第1个空白字符处停止读取(满足两个条件之一便停止)。
如果第1个非空白字符是A而不是数字,会发生什么情况?scanf()将停在那里,并把A放回输入中,不会把值赋给指定变量。程序在下一次读取输入时,首先读到的字符是A。如果程序只使用%d转换说明, scanf()就一直无法越过A读下一个字符。另外,如果使用带多个转换说明的scanf(),C规定在第1个出错处停止读取输入。
用其他数值匹配的转换说明读取输入和用%d 的情况相同。区别在于scanf()会把更多字符识别成数字的一部分。例如,%x转换说明要求scanf()识别十六进制数a~f和A~F。浮点转换说明要求scanf()识别小数点、e记数法(指数记数法)和新增的p记数法(十六进制指数记数法)。
如果使用%s 转换说明,scanf()会读取除空白以外的所有字符。scanf()跳过空白开始读取第 1 个非空白字符,并保存非空白字符直到再次遇到空白。这意味着 scanf()根据%s 转换说明读取一个单词,即不包含空白字符的字符串。如果使用字段宽度,scanf()在字段末尾或第1个空白字符处停止读取。无法利用字段宽度让只有一个%s的scanf()读取多个单词。最后要注意一点:当scanf()把字符串放进指定数组中时,它会在字符序列的末尾加上’\0’,让数组中的内容成为一个C字符串。
scanf()函数使用空白(换行符、制表符和空格)把输入分成多个字段。在依次把转换说明和字段匹配时跳过空白。
除了%c,其他转换说明都会自动跳过待输入值前面所有的空白。
#include <stdio.h>
int main(void)
{
int age; // 变量
float assets; // 变量
char pet[30]; // 字符数组,用于储存字符串
printf("Enter your age, assets, and favorite pet.\n");
scanf("%d %f", &age, &assets); // 这里要使用&
scanf("%s", pet); // 字符数组不使用&
printf("%d $%.2f %s\n", age, assets, pet);
getchar();/*方便看结果,根据需要调节数量*/
getchar();
getchar();
return 0;
}
/*格式字符串两数据间使用空格或者使用多个scanf()函数分别输入数据时,输入形式较自由,可以
随意使用空白。scanf()函数使用空白(换行符、制表符和空格)把输入分成多个字段。在依次把转
换说明和字段匹配时跳过空白。
vs2017输入输出结果:
Enter your age, assets, and favorite pet.
18
89640.763
panda
18 $89640.77 panda
*/
/*数据不匹配时输出结果,程序只读取了Panda这个输入
vs2017输入输出结果:
Enter your age, assets, and favorite pet.
Panda 18 5634.12
-858993460 $-107374176.00 Panda
*/
二、格式字符串中的普通字符
scanf()函数允许把普通字符放在格式字符串中。除空格字符外的普通字符必须与输入字符串严格匹配。
1、scanf()中除c%外的其它转换说明
两数据之间只有空格:(输入方式自由,可随意使用空白)
#include <stdio.h>
int main(void)
{
int n, m;
scanf("%d %d",&n,&m);/*注意在两个输入数据之间使用空格,在输入数据时较自由,可以
在两个数据之间使用任意数量的空白(换行符、制表符和空格)*/
printf("**%d**%d**\n",n,m);
getchar();/*方便看结果,根据需求选择数量*/
getchar();
return 0;
}
/*vs2017输入输出结果:
78
89
**78**89**
*/
两数据之间只有逗号:(逗号必须紧跟第一个数据)与逗号之后第二个数据之前有空格结果相同
#include <stdio.h>
int main(void)
{
int n, m;
printf("0123456789\n");
scanf("%d,%d", &n, &m);/*两数据之间只有逗号*/
printf("**%d**%d**\n", n, m);
getchar();/*方便看结果,根据需求选择数量*/
getchar();
getchar();
return 0;
}
/*逗号与第一个数据之间没空格。
vs2017输入输出结果:
0123456789
32,
56
**32**56**
*/
/*逗号与第一个数据之间有空格,输出错误
vs2017输入输出结果:
0123456789
32 , 56
**32**-858993460**
*/
逗号之前有空格:
a.逗号之前第一个数据之后有空格
#include <stdio.h>
int main(void)
{
int n, m;
printf("0123456789\n");
scanf("%d ,%d", &n, &m);/*第一个数据之后逗号之前有空格*/
printf("**%d**%d**\n", n, m);
getchar();/*方便看结果,根据需求选择数量*/
getchar();
getchar();
return 0;
}
/*数据之间没空格。
vs2017输入输出结果:
0123456789
32,56
**32**56**
*/
/*数据与逗号之间有空格
vs2017输入输出结果:
0123456789
32 , 56
**32**56**
0123456789
32 ,
56
**32**56**
*/
b.第一个数据之前有空格(结果与两数据之间只有逗号相同)
#include <stdio.h>
int main(void)
{
int n, m;
printf("0123456789\n");
scanf(" %d,%d", &n, &m);/*第一个转换说明之前有空格*/
printf("**%d**%d**\n", n, m);
getchar();/*方便看结果,根据需求选择数量*/
getchar();
getchar();
return 0;
}
/*数据之间没空格。
vs2017输入输出结果:
0123456789
32,56
**32**56**
*/
/*逗号紧跟第一个数据
vs2017输入输出结果:
0123456789
32,
56
**32**56**
*/
/*逗号与第一个数据之间有空格,输出出错
vs2017输入输出结果:
0123456789
32 , 56
**32**-858993460**
*/
普通字符之间有空格是否输入自由:
a.普通字符之间没空格:
#include <stdio.h>
int main(void)
{
int n, m;
printf("0123456789\n");
scanf("%d,,%d", &n, &m);/*两逗号之间没空格*/
printf("**%d**%d**\n", n, m);
getchar();/*方便看结果,根据需求选择数量*/
getchar();
getchar();
return 0;
}
/*数据之间没空格。
vs2017输入输出结果:
0123456789
23,,56
**23**56**
*/
/*逗号之间有空格,输出出错
vs2017输入输出结果:
0123456789
23, ,56
**23**-858993460**
*/
b.普通字符之间有空格
#include <stdio.h>
int main(void)
{
int n, m;
printf("0123456789\n");
scanf("%d, ,%d", &n, &m);/*两逗号之间有空格*/
printf("**%d**%d**\n", n, m);
getchar();/*方便看结果,根据需求选择数量*/
getchar();
getchar();
return 0;
}
/*逗号之间没空格。
vs2017输入输出结果:
0123456789
23,,56
**23**56**
*/
/*逗号之间有空格
vs2017输入输出结果:
0123456789
23,
,
56
**23**56**
*/
空白(换行符、制表符和空格)
小节总结:格式字符串中不带c%这个转换说明,除空格外的普通字符其前一个字符是空格,则其输入自由(其前可随意使用空白);除空格外的普通字符其前一个字符不是空格,则其输入要严格按照格式;除c%外的转换说明输入自由(其前可随意使用空白)。
2、scanf()带有%c这个转换说明
c%前有空格(一个转换说明前有空格)
#include <stdio.h>
int main(void)
{
char n, m;
printf("0123456789\n");
scanf("%c %c",&n,&m);/*两转换说明之间有空格*/
printf("**%c**%c**\n",n,m);
getchar();/*方便看结果,根据需求选择数量*/
getchar();
getchar();
return 0;
}
/*a、b之间有一个空格
vs2017输入输出结果:
0123456789
a b
**a**b**
*/
/*转换说明之前有空格,输入自由(可使用空白)
vs2017输入输出结果:
0123456789
a
b
**a**b**
*/
/*先读取了一个空格给n,因为第二个转换说明前有空格,跳过其它空白(换行符、制表符和空格)取a给m。
vs2017输入输出结果:
0123456789
a b
** **a**
*/
c%前有空格(两个转换说明前都有空格)(可以自由使用空白)
#include <stdio.h>
int main(void)
{
char n, m;
printf("0123456789\n");
scanf(" %c %c",&n,&m);/*两转换说明之前都有空格*/
printf("**%c**%c**\n",n,m);
getchar();/*方便看结果,根据需求选择数量*/
getchar();
getchar();
return 0;
}
/*ab可以自由输入
vs2017输入输出结果:
0123456789
a
b
**a**b**
*/
格式字符串带有除空格外的其它普通字符
#include <stdio.h>
int main(void)
{
char n, m;
printf("0123456789\n");
scanf(" %c ,%c",&n,&m);/*第一个转换说明之前有空格,第二个转换说明前只有逗号,逗号与第一个转换说明之间有空格*/
printf("**%c**%c**\n",n,m);
getchar();/*方便看结果,根据需求选择数量*/
getchar();
getchar();
return 0;
}
/*逗号和b紧挨着
vs2017输入输出结果:
0123456789
a
,b
**a**b**
*/
/*逗号和b之间有空格。第一个转换说明前有空格,跳过一些空白,取a给n;逗号与第一个转换说明
之间有空格,因此其输入自由(其前可用空白);第二个转换说明与逗号之间没空格,因此取紧挨逗
号的空格给m。
vs2017输入输出结果:
0123456789
a
, b
**a** **
*/
空白(换行符、制表符和空格)
小节总结:格式字符串中带有c%这个转换说明,c%前一个字符是空格,其输入自由(其前可随意使用空白);c%前一个字符不是空格,其输入要严格按照格式;除空格外的普通字符前一个字符是空格,其输入自由(其前可随意使用空白);除空格外的普通字符前一个字符不是空格,其输入要严格按照格式。
混合输入测试:
#include <stdio.h>
int main(void)
{
char n, m;
char p[30],q[30];
int i;
float j;
scanf(" %c , %c", &n, &m);
scanf("%s ,%s", p, q);
scanf("%d, , ,%f", &i, &j);/*第一个逗号前一个字符不是空格,输入要严格按照规则(即逗号要紧跟数据之后输入)*/
printf("**%c**%c**\n", n, m);
printf("**%s**%s**\n", p,q);
printf("**%d**%f**\n", i, j);
getchar();/*方便看结果,根据需求选择数量*/
getchar();
getchar();
return 0;
}
/*
vs2017输入输出结果:
a ,
b
panda ,
apple
36, , ,
560.4
**a**b**
**panda**apple**
**36**560.400024**
*/
全篇总结:scanf()函数格式字符串中,除空格外的普通字符和c%这个转换说明输入规则相同,其前一个字符是空格,则其输入自由(其前可随意使用空白),其前一个字符不是空格,则其输入要严格按照格式;除c%外的转换说明输入自由(其前可随意使用空白)。