C 中scanf ( ) 函数用法 用法

我觉得,在输入输出函数中,scanf()函数,应该是最麻烦的,有时它给我们的结果很可笑,但是一定是一原因的....

首先声明一下,这篇日志不是介绍scanf()中各种格式符用法的文章(没有这个必要,但是大家一定要会用).

我尝试了很多种输入,包括一些错误的练习,曾经对scanf()由迷茫转向清醒,又由清醒再次转向迷茫......不知道何时是个尽头,谁让C如此高深呢?

在这里贴出来,也是想让自己时而不时能看到,也想知道自己的理解是否有错,错在哪里(所以我就厚着脸皮,放在上面了).

注意 , 键盘缓冲区 与输入有着密切的关系 ,并且, 类型匹配 对 输入也极为重要!!

下面进入主题:

scanf对流的操作遵从类型匹配操作原则,如果类型不匹配,它将不读取输入流。 因此输入流将滞留,如果输入流不空,scanf不会等待用户输入,直接从缓冲区中输入. 
但是,scanf() 怎样匹配? stdin又是什么? 
在网上搜到的关于匹配的非常少,有些细节原因还是找不到.
所以,我自作主张的下了点结论:
例: scanf("%d,%d",&i,&j);  输入:12 ,13回车 但是,j!=13. //注意,12后有一个空格,why?   
    原因:我解释为,在scanf()中,格式字符串中普通字符(不包括空白字符)实行的是 严格匹配,因为格式串中%d后面是一个 ','  ,因此输入中数字12后必须为一个','.

      scanf("1123%s",&str);  输入:1123aaabb 时str为 aaabb,但是,输入 24aabbdd时,   会出错,因为1123必须进行严格匹配.

另外:  scanf("%d/n",&i); printf("i=%d",i);      要怎么输入才能输出: i=12    ? 它不是你想像中的那样,有机会尝试一下吧!疑问 
一些样例:
scanf()是一个有返回值 的函数,它的返回值是什么?怎么样利用这个特性?
scanf()中的匹配原则: 在本文 第五点 具体说明... 
scanf()中各种数据格式匹配的开始条件,结束条件 . 
         如: %d ,/n等类型输入结束条件.

          scanf("%d/n",&i);printf("%d",i);  输入 12回车,并无输出,why? 
scanf()函数的结束条件: 当各个 格式符 匹配完毕,且最后有一个回车时,函数结束.
scanf("%s",str)连续输入127个就不能继续输入了.  //TC中,VC好像是4000多..

//说明键盘缓冲区长度为一个字节吗?但是  stdin->bsize(缓冲区大小)事实上为 512,这又是为什么?
stdin缓冲区中的数据残留 :  scanf("%3s",str); c= getchar();  输入: aaabbccc回车,  此时str="aaa",c='b';  //缓冲区中数据残留! 
getch()不经过缓冲区,直接接收键盘上输入的字符. 
         //在上例中,加上一个 ch=getch();  但是getch()并不能读取bbccc中的任何一个,说明 getch()与getchar()并不一样,并且它们对Enter读取的值也不同!
一个不常用的格式符:    %[]  ,如  scanf("%[a-z]",str);   

输入: abcdefdsaABCDEF  输出:str="abcdefdsa" ;
怎么用scanf()来输入一个有空格的字符串?    疑问 
scanf()处理时,一个Enter送到缓冲区中有两个值 :   一个回车(10) ,一个换行(13). 可以用 getchar()来接收(但是,在只能接收到/n,即13).
在一个scanf()函数之后加个fflush(stdin)可以清除输入数据残留?   
   scanf("%3s",str); fflush(stdin); c=getchar();

   直接输入 aaabbbddd回车, c还能取得值吗? 
下面是详细解释:
scanf()函数执行成功时的返回值是成功读取的变量数 , 也就是说,你这个scanf()函数有几个变量,如果scanf()函数全部正常读取,它就返回几。但这里还要注意另一个问题,如果输入了非法数据,键盘缓冲区就可能还个有残余信息问题。
scanf()- 函数是通用终端格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。可以读入任何固有类型的数据并自动把数值变换成适当的机内格式。
sscanf() - 从一个字符串中读进与指定格式相符的数据.
函数原型:
Int  sscanf( string str, string fmt, mixed var1, mixed var2 ... );
int  scanf( const char *format [,argument]... );
sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。
其中的format可以是一个或多个 {%[*] [width] [{h | l | I64 | L}]type | ' ' | '/t' | '/n' | 非%符号}
如:  sscanf("123456", "%s", buf);  
     puts(buf);  //结果为: 123456
下面主要说一下scanf()的用法:
scanf函数的一般形式
scanf(格式控制,地址表列)
int scanf(char *format[,argument,...]);
“格式控制”的含义同printf函数;“地址表列”是由若干个地址组成的表列,可以是变量的地址,或字符串首地址。
scanf()函数返回成功赋值的数据项数,出错时则返回EOF。 
注:  scanf()中空白字符(包括/n,space)会使scanf()函数在读操作中略去输入中的零个或者一个或者多个空白字符,空白符可以是space,tab,换行 等等,直到第一个非空白符出现为止。//下一个格式符为%c也同样如此. 如,scanf("%d %c",&i,&ch); 输入:11             A回车,i=11,ch=A. 这里ch并不为空格. 
     一个非空白字符会使scanf()函数在读入时剔除掉与这个非空白字符相同的字符。
     如:  scanf("   %c",&ch);  输入: 若干个回车后,输入 A,     ch=A.  
          scanf("5729%s",str); 输入:  5729okok              str=okok.  但是请注意:当输入的前几个不是5729时(就算以空格开始也不行!),将会出错,str值不变.  
scanf()中 格式字符 说明
    %p 读入一个指针 
    %[] 扫描字符集合  
    %n 至此已读入值的等价字符数 
    %s 读入一个字符串,遇空格、制表符或换行符结束。
    %c   %d   %i   %o  %x   %X  %c   
%f 输入实数,可以用小数形式或指数形式输入。
%F   %e   %E   %g   %G  
%u 读入一个无符号十进制整数  
%% 读%符号
附加格式说明字符表修饰符 说明
    L/l 长度修饰符 输入"长"数据
h 长度修饰符 输入"短"数据
W 整型常数 指定输入数据所占宽度
m指定输入数据所占的宽度
* 星号 空读一个数据
结合实际例程,一一阐述:
一:   "%d%d%d"
         是按十进值格式输入三个数值。输入时,在两个数据之间可以用一个或多个空格、tab键、回车键分隔。
       "%d,%d,%d" 
运行时按如下方式输入三个值:3,4,5 ↙(输入a,b,c的值)或者3,□4,□5 ↙(输入a,b,c的值)3,□□□4,□5 ↙(输入a,b,c的值) 
................

都是合法的,但是输入3□,4□□,5 ↙ 出错!!!!  

    //因为scanf()的格式串中普通字符实行完全匹配!
          %c 
       在用"%c"输入时,空格和“转义字符”均作为有效字符。

二:    scanf()函数以一个非空白字符(包括空格,跳格,回车)开始一个数据的输入 ( %c 当然除外!,但是注意,gets()以任意字符为开始! ). 
       scanf()函数接收输入数据时,遇以下情况结束一个数据的输入:(不是结束该scanf函数,scanf函数仅在每一个数据域均有数据,并按回车后结束)。 
    ① 遇空格、“回车”、“跳格”键。
② 遇宽度结束。
③ 遇非法输入。    //这里很重要,如果有非法输入,则结束这个数据的输入,但是输入的非法数据还在缓冲区中,你可以用对应的数据类型接收!也可以干脆清除缓冲区.
                       //     例如:
                                  i=10;
                                  scanf("%s",str);
                                  scanf("%d",&i);
                                  scanf("%s",str2);
                                  printf("%s/n",str);printf("%d/n",i);printf("%s/n",str2);
                         输入: i love!
                         输出: i
                                10

                                love!

    //因为 love对 %d来说是一个开始输入(scanf()以一个非空白开始),但是因为不合法,

      所以这个开始也就是结束,i值不变!                         
三 .   scanf()函数能不能正确接受有空格的字符串?如: I love c! 

      //事实上它可以!!!
     char str[80];
     scanf("%s",str);          //输入  I love c?   //结果: 输出 I
        分析:  scanf()扫描到"I"后面的空格就认为对str的赋值结束,并忽略后面的"love c!" . 这里要注意是"love  c!"还在键盘缓冲区
         经过调试发现,其实这时缓冲区字符串首尾指针已经相等了,也就是说缓冲区清空了,scanf()函数应该只是扫描stdin流,这个残存信息是在stdin中.

        //其实,我曾试过,用scanf("%s",str)连续输入127个字符后,键盘缓冲区就装不下了,也就是说,对输入的不做处理,继续输入,就没有反应了,只有输入回车才有效.

   来验证一下:                 
    #include <stdio.h>                                                                     
int main()
{
char str[80];
char str1[80];
char str2[80];
scanf("%s",str);          /*此处输入:I love you! */
printf("%s",str);
sleep(3);                   /*这里等待3秒,告诉你程序运行到什么地方*/
scanf("%s",str1);        /*这两句无需你再输入,是对键盘盘缓冲区再扫描 */
scanf("%s",str2);        /*这两句无需你再输入,是对键盘盘缓冲区再扫描 */
printf("/n%s",str1);
printf("/n%s",str2);
return 0;
}
        输入:I love  c!
输出:
      I
love
c!
   那么,怎么来输入一个有空格的字符串? 
   用gets()当然可以,但我们同样可以用 scanf(),因为,scanf()还有一个我们不常用的输入格式符:    "%[]" 
   特别的:%*[width] [{h | l | I64 | L}]type 表示满足该条件的被过滤掉,不会向目标参数中写入值
支持集合操作:    //类似于  正则表达式 .

     %[a-z] 表示匹配a到z中任意字符,贪婪性(尽可能多的匹配)      //  或:  %[a-z1-9] 遇到非 a~z,1~9的则结束.
     %[aB'] 匹配a、B、'中一员,贪婪性                     //  以非 a,b,' 的字符为结束.
     %[^a] 匹配非a的任意字符,贪婪性      scanf("%[^a]",str);  

    //  输入:  ffddssaaff   则提取 ffddss到字符串str中. 即扫描到a就立即作为结束.
  故,我们可以用 scanf("%[^/n]",str); 来输入一个有空格的字符串. //输入:l love c!回车,   则str中为 I love c!
     那么 scanf("%*[^/]/%[^@]",str); 的作用呢???  //输入一个字符串,截取 /到@之间的字符串...

四: 解决键盘缓冲区的污染问题.  //即 残余信息 ,这个很重要吧.
    例如:
         scanf("%c",&c1); 
         scanf("%c",&c2); 
      当输入: a回车b回车  输出c1,c2的值:很明显 c2不为b. 
    原因: 将c2用int表示出来,看看scanf()函数赋给C到底是什么,结果是 c2=10 .
          ASCII值为10是什么?换行即/n.
          我们每击打一下"Enter"键,向键盘缓冲区发去一个“回车”(/r),一个“换行"(/n).
          在这里 /r被scanf()函数处理掉了(姑且这么认为吧^_^),而/n被scanf()函数“错误”地赋给了c.  
                  // 好像用getch()时,击ENTER,接收的是回车,并且,它不从键盘缓冲区经过,即键盘缓冲区内容与其无关!!!!!
                       我做了个小试验:   
                          scanf("%3s",str);
                          puts(str);
                          c1=getch();
                          printf("c1 %d/n",c1);
                          c2=getchar();
                          printf("c2 %d/n",c2);
                      // 输入 aaabcdef 输出aaa 输入 A输出: 65,98  看到了吧!!! 
                      // %3s只接收 aaa,然后输入一个A,被getch()接收,输出之后,getchar()继续从缓冲区中取出 b ,明白了.
      解决这类问题最好的办法是:  可以在两个scanf()函数之后加个fflush(stdin);        //功能: 清除一个流 用法: int fflush(FILE *stream);
     另外,百度百科上还有另外一个方法:  用getchar()和getch()接收.     但是,通过上面那个实验我们可以看到,getch()并不对缓冲区作处理,并不能处理scanf()的残余信息. 
                //可以试一下:
                                           scanf("%c",c1);
                                           getch();   //假设用来接收换行.
                                           scanf("%c",c2);
               //输入 A回车后 : c1值为 65,c2 为13 ,即换行.    

               //实际上,getch()会等着一个输入.
               //而把getch()换为 getchar()后, 输入 :A回车B回车,输出 A B

五: 下面这段程序要输入两个数,程序才结束,而不是预期的一个,why?

#include<stdio.h>
  int main()
  {   
  int a;
  printf("input the data/n");
  scanf("%d/n",&a);       //这里多了一个回车符/n,如果用scanf("%d  ",&a);    printf("%d",a);               //也会出现同样问题.
  return 0;
  }
   //输入: 11回车  后,没有输出,再输入空格,回车,Tab 中任意多个,都没有输出,当输入非空白字符时如 输入 abc回车 ,才有输出,输出为11.
  //分析其原因(不一定准确,应该可以这么解释吧):
     scanf()是一个终端格式化输入函数,也就是说按匹配 对 变量进行赋值!! 
     规则 : 例如 对于 " %d/n" :

          第一个空格可以与输入缓冲区的 任意多个 空白字符匹配(包括空格,回车,Tab),当遇见第一个非空白字符时,结束其匹配,接着处理%d .
          %d可以与连续的数字符号进行匹配,当遇到第一个非数字符号时,结束匹配,若与其匹配的数字个数为0,则%d对应的变量值不变.//注意,%d与任意一个非int字符开始匹配失效,就算是 '.'也不例外,如输入 12.30则 .30不会被读取,而是留在缓冲区中. 
          同理,/n也要与 一个或者多个 Enter,Tab,space匹配,直到遇到第一个非 空白字符. 
          同样 对于 "%d":  与缓冲区中第一个非空白字符开始进行匹配.
          但是"%c"是个例外,它与缓冲区中的第一个字符就匹配,不论空白与否,所以,处理 输入的字符+Enter时,一定要请注意其中的Enter. 
         所以对上面的例子,输入为: 11a时,回车一次就可以输出11,但是不要忘了,缓冲区中还有 a和/n  !!!    

六:有关stdin, 事实上它就是一个标准输入文件, 为 File * 类型.

   因此, scanf("%s",str); 也就等价于 fscanf(stdin,"%s",str);

   但是 scanf()只能用来输入 127以下个字符,也就是说,缓冲区只能装下127个字符+'/',那为什么 stdin->bsize又为 512呢?   //在TC下.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值