由scanf说起之1:scanf函数和回车、空格 及其返回值

scanf和回车的关系:

  先看一个我们经常使用的代码:

  char c;
  scanf("%c", &c);

  scanf 的各种格式中,%d、%c、%s三种最常用,通常都是以回车作为一次输入的结束。由于对字符解析方式及字符特点不同(如数字中一般不会出现空格回车等特殊字符)不同,%d格式一般不会在连续输入时,遇到问题。而%s和%c却会出现各种各样的问题,比如回车问题,空格问题等。

我们已经习惯了上面的代码,再看下面存在连续输入时的情况

  int i;
  char c;
  scanf("%d", &i);
  scanf("%c", &c);

  如果我们为i赋值为3,并以回车的方式结束对i的赋值。再按常规思路为c赋值时,就会发现并不能为c赋值,并且c自动赋值为十进制的10,也就是回车键。why???

要回答这个问题,先看scanf函数的功能和工作原理

  scanf函数这个函数的作用是从标准输入设备获取输入值,并存储到参数列表中指针所指向的内存单元,如果读入成功,函数会返回读入成功的数据的个数;scanf函数的结束通常有3种,遇到空格、回车或者tab键为常用的结束方式;或者按照格式控制符的指定来控制结束,如%5d类的格式;遇到非法输入也会自动结束。

对常用的三种格式,结束符号分别如下:

%d格式输入,默认分隔符是所有的 white-spaces(空格、回车、制表);

%c格式输入,则按ASCII字符考虑,无分隔符。可能会受到之前输入的影响,必要时用fflush(stdin);清除缓冲区;

%s 是 字符串格式,默认分隔符是所有的 white-spaces,输入后自动加入结束符"\0"。

  继续上面的由于连续输入带来的问题,往深了说,就涉及到缓冲区了。scanf函数是以删除的方式从缓冲区读取数据(缓冲区中存储来自标准输入的数据)。如果缓冲区是空的,就阻塞之,等待从键盘输入;并且scanf还能对数字输入忽略先导的空白符,如\n\t和空格等(注意,对字符输入并不忽略先导字符,这个也是很自然的道理,因为\n\t和空格在字符中都是合法的字符)。

  scanf的缓冲机制和对字符的处理方式就造成了scanf对字符%c和字符串%s的读取时的各种意外。比如上面的例子,回头分析这段代码:

  输入了i的值为3然后按回车,当前缓冲区中数据为”3\n”,由回车的作用scanf开始从缓冲区中读取一个%d控制的数据,也就是3,此时缓冲区中还剩”\n”;对下一个scanf函数的格式控制是%c,这个情况下并不忽略先导的空白字符\n,而是直接赋给字符c了。想要查看缓冲区的内容,stdin[s1] ,如果想要查看当前stdin中的内容,一般方法都比较忤逆,可以尝试使用文件操作freopen将stdin中的数据重定向到另外一个file*中。这个另辟一文。

  如何解决这个缓冲区和字符解析的问题呢?既然缓冲区有我们不需要的东西,那就清除缓冲区。微软系统中是fflush(stdin)函数可以清除缓冲区,而有的编译系统并没有定义对stdin的fflush操作,就把stdin中的数据读出来:

1)  可以使用fgets()函数,这个函数没有编译器的限制;

2)  或者把缓冲区中多余的东西交给别的函数,如getchar(),具体代码为 while( (c = getchar() ) != '/n' && c != EOF );

3) 上面的方法基于原理,但有点麻烦,尤其是遇到字符%c和字符串%s对回车的处理时。C还提供了gets()函数解决了这个问题,gets()函数是不论中间有什么字符,一律读进来,直到遇到回车符;

4)  C++中,还可以操作stdin的指针,stdin是一个File*类型的数据结构,使stdin->_IO_read_ptr = stdin->_IO_read_end;。但是C中不可以。

 

scanf和空格键

  前面就有说,scanf函数,根据格式的不同,对空白字符的处理也不同。%d格式下,对空白字符不敏感,通常都是作为结束符的;对%c来说,对回车符比较敏感,空格的做为一个普通字符处理的;对%s来说,回车和空格都是当前函数的结束字符,由于缓冲区stdin机制,这里又要特别注意 空格和回车对%s的影响。

  %s默认分隔符是所有的 white-spaces,输入后自动加入结束符"\0",使其成为一个字符串(之所以加上\0,是和字符数组char[]的结束符有关的,C中是没有string这个类型的,是使用char[]结构实现字符串)。值得注意的是,即使输入字符的长度足够,%s是宁愿舍弃输入字符,也要把/0加上去的,作为字符串的结束。并且,scanf会忽略缓冲区开头的空格,知道遇到一个非空格字符,才开始向内存中读取数据。

  比如,我们想要输入”The C Programming Language.\n”,中间的空格怎么处理呢?

  其中一个解决方法是使用gets函数,这个函数是以回车符作为输入结束的标志的;还有一种解决方式是:scanf("%[^\n]", c)

  char c[15];
  scanf("%[^\n]", c[s2] );

  只是scanf("%[^\n]", c);和gets这两种处理方式都是不忽略所有的空格,包括缓冲区开头的空格;这一点和scanf("%s",c);的处理方式不同,%s的方式,忽略缓冲区开头的空格。

 

另:scanf的返回值

  scanf()函数返回的值为:正确按指定格式输入变量的个数;也即能正确接收到值的变量个数。在类型匹配错误的时候,以非正常的方式退出。可以利用scanf函数的返回值判断输入是否正确,并进行流程控制:

     int i = 0;
     char c1[15];
     while((scanf("%c", &c1[i])!=EOF) && i<14)
     {
              i++;
     }

 


 [s1]stdin是标准输入,一般针对键盘,是个FILE* 类型的数据。

 [s2]这里注意使用c 还是 &c:

          c是个数组,数组 即代表了数组的首地址,所以可以使用c;

          &c,表示数组c的地址,也就是指首地址,所以也是可以的。但通常c更符合逻辑一点。这在读取%s类型的数据时可以说明这一点:

                  scanf(“%s”, c);

 

转载于:https://www.cnblogs.com/czl-sy/archive/2013/04/07/3006109.html

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值