指引网

当前位置: 主页 > 编程开发 > C >

C语言中scanf函数格式化错误输入问题

来源:网络 作者:佚名 点击: 时间:2017-07-19 23:06
[摘要]  好久都么写文章了,一直忙着做课程设计,感觉有些懒了。今天实验室一个同学问到了这样一个问题。

函数名: scanf
功 能: 执行格式化输入
用 法: int scanf(char *format[,argument,...]);
scanf()函数是通用终端格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。可以读入任何固有类型的数据并自动把数值变换成适当的机内格式。
其调用格式为:      scanf("<格式化字符串>",<地址表>);
scanf()函数返回成功赋值的数据项数,出错时则返回EOF。
其控制串由三类字符构成:
1。格式化说明符;
2。空白符;
3。非空白符;

(A)                格式化说明符

格式字符           说明
%a                 读入一个浮点值(仅C99有效)
%A                 同上
%c                 读入一个字符
%d                 读入十进制整数
%i                 读入十进制,八进制,十六进制整数
%o                 读入八进制整数
%x                 读入十六进制整数
%X                 同上
%c                 读入一个字符
%s                 读入一个字符串
%f                 读入一个浮点数
%F                 同上
%e                 同上
%E                 同上
%g                 同上
%G                 同上
%p                 读入一个指针
%u                 读入一个无符号十进制整数
%n                 至此已读入值的等价字符数
%[]                扫描字符集合
%%                 读%符号
               
附加格式说明字符表
修饰符                       说明
L/l 长度修饰符               输入"长"数据
h 长度修饰符                 输入"短"数据
W 整型常数                   指定输入数据所占宽度
* 星号                       空读一个数据
hh,ll同上h,l但仅对C99有效。

(B)         空白字符
空白字符会使scanf()函数在读操作中略去输入中的一个或多个空白字符,空白符可以是space,tab,newline等等,直到第一个非空白符出现为止。
(C)        非空白字符
一个非空白字符会使scanf()函数在读入时剔除掉与这个非空白字符相同的字符

 代码如下 复制代码

#include    <stdio.h>
#include    <stdarg.h>
#include    "loc_incl.h"

int scanf(const char *format, ...)
{
    va_list ap;
    int retval;

    va_start(ap, format);

    retval = _doscan(stdin, format, ap);

    va_end(ap);

    return retval;
}

对于va_list、va_start、va_end等在stdarg.h头文件中定义的宏,都已经在(stdarg.h头文件源代码分析)一文中介绍过。

在上述代码中我们可以看到有一个_doscan函数,而这一函数在头文件loc_incl.h中定义,函数声明如下:

int _doscan(FILE * stream, const char *format, va_list ap);

_doscan函数的实现源代码如下:

 _doscan函数代码

 在上面的源代码中,值得注意的是第26行的getc宏,定义代码如下:

#define    getc(p)        (--(p)->_count >= 0 ? (int) (*(p)->_ptr++) :

                __fillbuf(p))
getc的调用形式:ch=getc(fp); 功能是从文件指针指向的文件读入一个字符,并把它作为函数值返回给int型变量ch。

第4行~第17行,定义一些后面需要用到的变量

第23行~第34行,跳过format格式串中的空格,并且跳过输入流中的空格

第37行~第42行,输入流stream与format格式串中的空白符(空白符可以是空格(space)、制表符(tab)和新行符(newline))保持一致

第44行~第52行,在format中的字符为'%'的前提下,stream中的字符也为'%',则继续

第54行~第57行,format当前字符为'*',表示读指定类型的数据但不保存

第58行~第62行,指定说明最大域宽。 在百分号(%)与格式码之间的整数用于限制从对应域读入的最大字符数于宽度

第64行~第282行,switch语句,用于格式修饰符,这些修饰符包括: h、l、L、c、p、b、d、i、o、u……,还有基于扫描集的'['修饰符

对scanf函数的源码分析,需要在scanf函数的语法格式详细的理解基础上进行,由于scanf函数实现十分复杂,需要仔细的品味,这里只是比较初步的分析,具体还有待后期不断的完善

格式化错误输入问题,例子

 代码如下 复制代码

#include <stdio .h>
 
int main()
 
{
 
 int i;
 
 do
 
 {
 
  scanf("%d",&i);
 
  switch(i)
 
  {
 
   case 1:printf("1n");break;
 
   case 2:printf("2n");break;
 
   case 3:printf("3n");break;
 
   default:printf("othern");
 
  }
 
 }while(1)
 
 return 0;
 
}
 
</stdio>

看起来就是每次输入一个整数,判断属于那种情况,再简单不过了。我们往往关注的是正确情况下的结果,但是如果我们错误的输入了,结果还会和预想的一样么?我先输了一次1,其他正确情况都判断过。然后输入了一个a。此时程序进入了死循环,重复输出1。什么原因呢?
我首先做了一个断点调试,发现在后来的几次输入中,scanf并没有把a的值赋值给i,也就是i的结果没有被覆盖。但是为什么我没有输入也会继续执行呢?这里肯定是sacnf对缓冲区的操作问题。知道问题的地方,scanf对错误的处理具体还是不知道。后来查了下,具体解释如下:
1、scanf对于正确的输入,会继续判断后面的是否符合格式输入,不是的话过滤掉,遇到回车就结束,然后把正确的字符从缓冲区取走,但是回车不取。
2、对于第一个就是错误的格式。如果是回车的话,会从缓冲区取走,但是直接丢弃。如果遇到不符合格式的,scanf就直接结束。
所以,对于该问题,后来缓冲区中的序列如下:n a n ,遇到n,取走,继续,遇到a,scanf结束。从此就是中被这个a给挡下来了,也就出现了死循环的问题。

------分隔线----------------------------