基础 - 字符读取函数scanf、getchar、gets、cin(清空缓存区解决单字符回车问题)

0x01 scanf、getchar、cin读取单字符:

如下:

//scanf读取字符 回车问题
void Sub_1_1()
{
    char v1,v2;
    scanf("%c", &v1);
    scanf("%c", &v2);
    printf("%d %d\n", v1, v2);

    //回车问题
}

/* scanf()和getchar()函数是从输入流缓冲区中读取值的,
    而并非从键盘(也就是终端)缓冲区读取。
    而读取时遇到回车(\n)而结束的,这个\n会一起读入输入流缓冲区的,
    所以第一次接受输入时取走字符后会留下字符\n,
    这样第二次的读入函数直接从缓冲区中把\n取走了,显然读取成功了,
    所以不会再从终端读取! */

//getchar读取字符 回车问题
void Sub_1_2()
{
    char ch1, ch2;
    ch1 = getchar();
    ch2 = getchar();
    printf("%d %d\n", ch1, ch2);

    //回车问题
}

//cin读取单字符 无回车问题
void Sub_1_3()
{
    char ch1, ch2;
    cin >> ch1;
    cin >> ch2;

    cout << ch1 << endl;
    cout << ch2 << endl;

}

例如:

Sub_1_1、Sub_1_2 输入 a,输出:

Sub_1_3输入a,输出:

 

为什么这个形式呢?

先说一下输入操作原理:程序的输入都建有一个缓冲区,即输入缓冲区。当一次键盘输入结束时会将输入的数据存入输入缓冲区,而cin函数直接从输入缓冲区中取数据。正因为cin函数是直接从缓冲区取数据的,所以有时候当缓冲区中有残留数据时,cin函数会直接取得这些残留数据而不会请求键盘输入,

 这里的10恰好是回车符,scanf()和getchar()函数是从输入流缓冲区中读取值的,而并非从键盘(也就是终端)缓冲区读取。而读取时遇到回车(\n)而结束的,这个\n会一起读入输入流缓冲区的,所以第一次接受输入时取走字符后会留下字符\n,这样第二次的读入函数直接从缓冲区中把\n取走了。

 

 0x02 scanf()和gets()读取字符串:

 1 //scanf()读取字符串 空格问题
 2 void Sub_2_1()
 3 {
 4     char str1[20], str2[20];
 5     scanf("%s",str1);
 6     printf("%s\n",str1);
 7     scanf("%s",str2);
 8     printf("%s\n",str2);
 9 
10     //空格问题 
11 
12     //hello world -> 
13     //hello
14     //world
15     /*第一次输入Hello world!后,
16     字符串Hello world!都会被读到输入缓冲区中,
17     而scanf()函数取数据是遇到回车、空格、TAB就会停止,
18     也就是第一个scanf()会取出"Hello",而"world!"还在缓冲区中,
19     这样第二个scanf会直接取出这些数据,而不会等待从终端输入。*/
20 
21     //scanf()读取字符串会舍弃最后的回车符!
22     //hello -> 
23     //hello -> 
24 
25 }
26 
27 //gets()读取字符串 接受空格
28 void Sub_2_2()
29 {
30     char str1[20], str2[20];
31     gets(str1);
32     printf("%s\n",str1);
33     gets(str2);
34     printf("%s\n",str2);
35 
36 }

 

 先来看Sub_2_1,程序的功能是读入一个字符串输出,在读入一个字符串输出。可我们会发现输入的字符串中不能出现空格,例如:

这个问题的原因跟0x01类似,第一次输入Hello world后,字符串Hello world都会被读到输入缓冲区中,而scanf()函数取数据是遇到回车、空格、TAB就会停止,也就是第一个scanf()会取出"Hello",而"world"还在缓冲区中,这样第二个scanf会直接取出这些数据,而不会等待从终端输入

 Sub_2_2,gets不会有这个问题:

 

 

 总结:


读取字符时:
scanf()以Space、Enter、Tab结束一次输入,不会舍弃最后的回车符(即回车符会残留在缓冲区中);
getchar()以Enter结束输入,也不会舍弃最后的回车符;
读取字符串时:
scanf()以Space、Enter、Tab结束一次输入
gets()以Enter结束输入(空格不结束),接受空格,会舍弃最后的回车符!
第二:为了避免出现上述问题,必须要清空缓冲区的残留数据,可以用以下的方法解决:
方法1:C语言里提供了函数清空缓冲区,只要在读数据之前先清空缓冲区就没问题了!
这个函数是fflush(stdin)。//似乎并没有用。

//setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区


方法2:自己取出缓冲区里的残留数据。

 

 源代码(包括解决单字符回车问题):

  1 #include <windows.h>
  2 #include <IOSTREAM>
  3 
  4 using namespace std;
  5 
  6 void Sub_1_1();
  7 void Sub_1_2();
  8 void Sub_1_3();
  9 
 10 void Sub_2_1();
 11 void Sub_2_2();
 12 
 13 int main ()
 14 {
 15     //scanf getchar 
 16     //Sub_1_1();
 17     Sub_1_2();
 18     //Sub_1_3();
 19     
 20 
 21 /*****/
 22     //scanf gets
 23 //    Sub_2_1();
 24     //Sub_2_2();
 25 }
 26 
 27 //setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区  
 28 
 29 //scanf读取字符 回车问题
 30 void Sub_1_1()
 31 {
 32     char v1,v2;
 33     
 34     scanf("%c", &v1);
 35     //setbuf(stdin, NULL); //解决回车问题
 36     scanf("%c", &v2);
 37     //setbuf(stdin, NULL); //解决回车问题
 38     printf("%d %d\n", v1, v2);
 39 
 40     //回车问题
 41 }
 42 
 43 /* scanf()和getchar()函数是从输入流缓冲区中读取值的,
 44     而并非从键盘(也就是终端)缓冲区读取。
 45     而读取时遇到回车(\n)而结束的,这个\n会一起读入输入流缓冲区的,
 46     所以第一次接受输入时取走字符后会留下字符\n,
 47     这样第二次的读入函数直接从缓冲区中把\n取走了,显然读取成功了,
 48     所以不会再从终端读取! */
 49 
 50 //getchar读取字符 回车问题
 51 void Sub_1_2()
 52 {
 53     char ch1, ch2;
 54     //setbuf(stdin, NULL); //解决回车问题
 55     ch1 = getchar();
 56     //setbuf(stdin, NULL); //解决回车问题
 57     ch2 = getchar();
 58     printf("%d %d\n", ch1, ch2);
 59 
 60     //回车问题
 61 }
 62 
 63 //cin读取单字符 无回车问题
 64 void Sub_1_3()
 65 {
 66     char ch1, ch2;
 67     cin >> ch1;
 68     cin >> ch2;
 69 
 70     cout << ch1 << endl;
 71     cout << ch2 << endl;
 72 
 73 }
 74 
 75 //scanf()读取字符串 空格问题
 76 void Sub_2_1()
 77 {
 78     char str1[20], str2[20];
 79     scanf("%s",str1);
 80     printf("%s\n",str1);
 81     scanf("%s",str2);
 82     printf("%s\n",str2);
 83 
 84     //空格问题 
 85 
 86     //hello world -> 
 87     //hello
 88     //world
 89     /*第一次输入Hello world!后,
 90     字符串Hello world!都会被读到输入缓冲区中,
 91     而scanf()函数取数据是遇到回车、空格、TAB就会停止,
 92     也就是第一个scanf()会取出"Hello",而"world!"还在缓冲区中,
 93     这样第二个scanf会直接取出这些数据,而不会等待从终端输入。*/
 94 
 95     //scanf()读取字符串会舍弃最后的回车符!
 96     //hello -> 
 97     //hello -> 
 98 
 99 }
100 
101 //gets()读取字符串 接受空格
102 void Sub_2_2()
103 {
104     char str1[20], str2[20];
105     gets(str1);
106     printf("%s\n",str1);
107     gets(str2);
108     printf("%s\n",str2);
109 
110 }
111 
112 
113 //总结
114 /*
115 读取字符时:
116 scanf()以Space、Enter、Tab结束一次输入,不会舍弃最后的回车符(即回车符会残留在缓冲区中);
117 getchar()以Enter结束输入,也不会舍弃最后的回车符;
118 读取字符串时:
119 scanf()以Space、Enter、Tab结束一次输入
120 gets()以Enter结束输入(空格不结束),接受空格,会舍弃最后的回车符!
121 第二:为了避免出现上述问题,必须要清空缓冲区的残留数据,可以用以下的方法解决:
122 方法1:C语言里提供了函数清空缓冲区,只要在读数据之前先清空缓冲区就没问题了!
123 这个函数是fflush(stdin)//似乎并没有用。
124 //setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区 
125 方法2:自己取出缓冲区里的残留数据。
126 */

 

posted on 2017-10-19 23:38  吱昂  阅读(1944)  评论(0编辑  收藏  举报