没想到你是这样的scanf!

目录

一、是什么

二、格式指令

①格式说明符

②长度修饰符

 ③*舍弃

 ④域宽

三、注意点

1.输入格式相同

2.空白符

 3.安全问题

扫描二维码关注公众号,回复: 13474744 查看本文章

 四、问题分析

 问题一:键盘缓冲区残余信息问题

 问题二:格式匹配问题①

 问题三:格式匹配问题②

 问题四:用scanf接收带空格的字符串问题

 问题五:格式不匹配带来的stdin流阻塞问题 


一、是什么

1.函数 scanf() 是从标准输入流stdio (标准输入设备,一般指向键盘)中读内容的通用子程序,根据说明的格式读入多个字符,并保存在对应地址的变量中。

2.函数的第一个参数是格式字符串,它指定了输入的格式,并按照格式说明符解析输入对应位置的信息并存储于可变参数列表中对应的指针所指位置。每一个指针要求非空,并且与字符串中的格式符一一顺次对应。 

int main()
{
	int a,i; 
	char c;
	int arr[5] = { 0 };
	scanf("%d",&a);//输入的是地址,所以要有&
	scanf("%c。",&c);//输入要求格式中有"。",但他与c的格式要求不符,读入但被舍弃,必要但不重要。
	for (i = 0; i < 5; i++)
		scanf("%c",&arr[i]);//也可以将输入的值传到数组中去
	return 0;
}

3.由图一中我们可以看出scanf是有返回值的,其返回值为读入的项数。如果遇到错误或遇到end of file,返回值为EOF。end of file为Ctrl+z 。

[小知识:while(~scanf("%d",&n))等价于while(scanf("%d",&n)!=EOF)]

int main()
{
	int i = 0;
	while (scanf("%d", &i) != EOF)
	{
		printf("hello bit");;
	}

}

 输入Ctrl+z 或者Ctrl+c回车后即可跳出循环。


二、格式指令

附上图表总结,便于大家梳理(网图,侵删!)

①格式说明符

②长度修饰符

 ③*舍弃

 百分号(%)与格式符之间的星号(*)表示读入指定类型的数据但不保存。 scanf() 返回等于成功赋值的域数的值,但由于星号修饰符而读入未赋值的域不计算在内。

 ④域宽

 在百分号(%)与格式码之间的整数用于限制从对应域读入的最大字符数。例如,希望向 address 读入不多于 20 个字符时,可以书写成如下形式:

scanf("%20d",&address);

如果输入流的内容多于 20 个字符,则下次 scanf() 从此次停止处开始读入。 若达到最大域宽前已遇到空白符,则对该域的读取立即停止;此时,scanf() 跳到下一个域。


三、注意点

1.输入格式相同

 如果scanf中%d是连着写的如“%d%d%d”,在输入数据时,数据之间不可以用逗号分隔,只能用空白字符(空格或tab键或者回车键)分隔——“2 (空格)3(tab) 4” 或 “2(tab)3(回车)4”等。若是“%d,%d,%d”,则在输入数据时需要加“,”,如“2,3,4”)。 如果格式控制串中有非格式字符则输入时也要输入该非格式字符,读入后被舍弃。

2.空白符

控制串中的空白符使 scanf() 在输入流中跳过一个或多个空白行。空白符可以是空格(space)、制表符(tab)和新行符(newline)。 本质上,控制串中的空白符使 scanf() 在输入流中读取,但不保存结果,直到发现非空白字符为止。

数据项必须由空白符( 空格(space)、制表符(tab)和新行符(newline))分割。逗号和分号等不是分隔符。C编译在碰到空格,TAB,回车或非法数据(如对“%d”输入“12A”时,A即为非法数据)时即认为该数据结束

虽然空格、制表符和新行符都用做域分割符号,但读单字符操作中却按一般字符处理。在输入字符数据(%c)时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符

现以scanf()根据一个%d转换说明读取一个整数为例: scanf()函数每次读取一个字符, 跳过所有的空白字符, 直至遇到第一个非空白字符才开始读取.。因为要读取整数, 所以scanf()希望发现一个数字字符或者一个符号(+或-)。如果遇到一个数字或符号, 它便保存该字符, 并读取下一个字符.。如果下一个字符是数字, 它便保存该数字并读取下一个数字。scanf()不断第读取和保存, 直到遇到非数字字符。如果读取到一个非数字字符, 它便认为读到了整数的末尾。然后,scanf()把非数字字符放回输入。 这意味着程序在下一次读取输入时, 首先读到的是上一次丢弃的非数字字符。scanf()计算已读取数字相应的数值, 并将计算后值放入指定的变量中。

 3.安全问题

在vs编译器下,库函数scanf是被认为不安全的,需使用scanf_s。然而使用scanf_s会降低程序的可移植性,在其他编译器下可能无法识别。为了避免警告,可以添加以下的代码:

#define _CRT_SECURE_NO_WARNINGS


 四、问题分析

 问题一:键盘缓冲区残余信息问题

 分析:①在空白符的分析中提到,上一次回车被下一次的输入读取

            ②进行单字符操作时若格式控制串中无非格式字符,则认为所有输入的字符均有效

int main()
{
	int a; char b;
	scanf("%d",&a);
	//解决方法一:构造加空白符的结构
	scanf(" %c",&b);
	//解决方法二:用getchar接收走回车
	getchar();
	scanf("%c",&b);
	return 0;
}

问题二:格式匹配问题①

细心的你可能已经发现了,在scanf 的格式后多了一个“\n”。在输入1的时候,回车没有被当成指定的格式处理,所以格式仍不匹配。当我们再次随便输入一个数据并且按下回车时,这是回车才被作为格式处理。

问题三:格式匹配问题②

 分析:和上面分析类似,%d后面有一个空格,所以需要再次输入以匹配格式

 问题四:用scanf接收带空格的字符串问题

 借这个问题分析一下上表没有提到的格式控制符[]

①[^scanfset]:(scanfset表示内容)遇到和[]内scanfset相同的字符就停止

②[scanfset]:遇到和[]内scanfset不同的字符就停止

③“-”字符:“-”表示匹配从其左边的字符到右边字符之间所有的字符(按ASCII码排序)

如A-Z表示匹配所有A到Z范围内的字符      1-9表示匹配所有1到9范围内的字符

int main()
{
	char str[20] = { 0 };
	char str1[20] = { 0 };
	char str2[20] = { 0 };
	scanf("%[^\n]",str);//遇到\n才停止
	scanf(" %[abc]",str1);//只接收abc
	scanf(" %[1-9]",str2);//只接收1~9的数
}

问题五:格式不匹配带来的stdin流阻塞问题 

int main()
{
	int ret = 0; int a, b, c;
	ret = scanf("%d %d %d", &a, &b, &c);
	printf("第一次接收的数据个数为%d\n",ret);
	ret = scanf("%d %d %d", &a, &b, &c);
	printf("第二次接收的数据个数为%d\n", ret);
	return 0;
}

 正常运行时的结果:

 如果我们不小心输入“1 b 3”,结果为:

 原因分析:

执行到第一个scanf时,当输入字符’b’的时候与ret=scanf("%d%d%d",&a,&b,&c);中的格式化字符串不匹配,stdin流被阻塞,scanf函数不在读取后面的部分,直接将1返回,表示只将stdin流中的1读入到了变量a中。

执行到第二个scanf时,stdin流没有被清空,字符’b’还是与格式化字符串不匹配,stdin流仍然被阻塞,所以没有提示输入,scanf函数将0返回。

 解决办法:

①在scanf函数后正确使用fflush(stdin);,清空输入缓冲区,不会影响下一次输入

②考虑到平台移植性,用getchar其实更好

int main()
{
	int ret = 0; int a, b, c;
	ret = scanf("%d %d %d", &a, &b, &c);
	printf("第一次接收的数据个数为%d\n",ret);
	fflush(stdin);
	ret = scanf("%d %d %d", &a, &b, &c);
	printf("第二次接收的数据个数为%d\n", ret);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/whc18858/article/details/120338980