getch(),getche(),getchar(),gets(),scanf()的区别,综合很多博客的文章。
getch()和getchar()
getchar()是C的标准库函数,包含在头文件<stdio.h>中,而getch()和getche()需要的头文件是<conio.h>。conio.h不是C标准库中的头文件。conio是Console Input/Output(控制台输入输出)的简写,其中定义了通过控制台进行数据输入和数据输出的函数,主要是一些用户通过按键盘产生的对应操作
getchar
This is a standard function that gets a character from the stdin.
getch
This is a nonstandard function that gets a character from keyboard, does not echo to screen.
getche
This is a nonstandard function that gets a character from the keyboard, echoes to screen.
Use getchar if you want it to work on all compilers. Use getch or getche on a system that supports it when you want keyboard input without pressing [Enter].And note that the return value of all three is int! You need this to properly check for EOF.
int getch( void ); Get a character from the console without echo
int getchar ( void ); Get character from stdin, Returns the next character from the standard input (stdin).It is equivalent to getc with stdin as its argument
可以看到,两个函数都没有参数,都是读入一个字符,返回值都是int型。但是注意getch()是从console读取,而getchar()是从stdin,一般是指键盘。要试验两者的区别,你可以写一个getch()看看不从键盘输入,比如直接用鼠标复制一段字符粘贴到console上,你会看到getch()会立即返回,但getchar一直等待,直到遇到 ENTER 键。
getch()读到一个字符就返回,不管是什么字符,所以,在TC时代经常被放在程序最后面,达到“按任意键退出”的效果。而getchar()直到遇见ENTER 键才返回,返回值是第一个字符,但是后面的字符也不会被丢弃,而是保存在一个缓冲区内。
[cpp] view plain copy
#include <stdio.h>
#include <conio.h>
int main()
{
int ch, cha;
ch = getch();
cha = getchar();
printf("ch=%d, cha=%d/n",ch,cha);
getch();
return 0;
}
输入:ENTER ENTER
输出:ch=13, cha=10
这段代码是在windows平台下,才会产生如上所说的差异。原因是windows平台下ENTER键会产生两个转义字符 /r/n, 因此,getch()读到 /r 时就会返回他的ASCII码13。
奇怪的问题是为什么getchar()会返回10呢?前面不是说过返回第一个字符吗?
这的确会让人费解。实际上产生这个结果的原因是,**getchar()把输入的 /r/n 转换成了 /n ,所以返回的是 /n 的ASCII码 10。**为什么会这样呢?因为前面说过getchar()是C语言标准库函数,而在unix系统中ENTER键只产生 /n 。顺便说一下,在MAC OS中ENTER键将产生 /r ,同样也会被替换成 /n。这样,不管在什么平台下,getchar()都会得到相同的结果,所以说getchar()标准库函数。
输入输出缓冲区的概念(C++用的多一些)
我想以一个例子说明,比如我想把一篇文章以字符序列的方式输出到计算机显示器屏幕上,那么我的程序内存作为数据源而显示器驱动程序作为数据目标,如果数据源直接对数据目标发送数据的话。数据目标获得第一个字符,便将它显示。然后从端口读取下一个字符,可是这时就不能保证数据源向端口发送的恰好是第二个字符(也许是第三个,而第二个已经在数据目标显示时发送过了)。这样的话就不能保证输出的数据能完整的被数据目标所接受并处理。
为了解决这个问题,我们需要在数据源与数据目标中间放置一个保存完整数据内容的区域,那就是 “缓冲区”。这样的话, 数据源可以不考虑数据目标正在处理哪部分数据,只要把数据输出到缓冲区就可以了,数据目标也可以不考虑数据源的发送频率,只是从缓冲区中依次取出下一个数据。从而保证了数据发送的完整性,同时也提高了程序的效率。
当然getch(),getche()没有用到缓冲区。
getch()实际是一个输入命令,作用是从键盘接收一个字符,而且并不把这个字符显示出来,就是说,你按了一个键后它并不在屏幕上显示你按的什么,而继续运行后面的代码,所以我们在C++中可以用它来实现“按任意键继续”的效果,即程序中遇到getch();这行语句,它就会把程序暂停下来,等你按任意键,它接收了这个字符键后再继续执行后面的代码。
你也许会问,为什么我们在C++中就没有在程序的末尾加上getch(),解释是,软件总是不断更新的,不好的地方当然要进行改正,getch()加在程序末尾,它又不赋值给任何变量,所以它在这个地方完全是垃圾代码,与程序无关。C++中考虑到这一点,于是在每次程序运行完了并不退出,而是自动把屏幕停下来,并显示“press any key…”叫你按任意键退出,这就好比C++在它的环境中运行程序,在程序的末尾自动加上了一行getch();语句,并且在这行语句前还添加了一行输出语句cout<<“press any key…”;来提示你程序结束了,按任意键继续。
实际上我们编译好的程序在程序结束了本身是不会停下来的,我们可以在编译产生的Debug目录中找到这个编译好的应用程序(扩展名exe),在文件夹中双击运行它,你会发现屏幕闪了一下MS-DOS窗口就关闭了,因为程序运行完就自动退出了,回到了windows环境,当然,如果我们在DOS环境中运行这个程序,我们就可以直接在看到DOS屏幕上看到程序运行结果,因为程序运行完后并不清屏。
getche()和getch()很相似,它也需要引入头文件conio.h,那它们之间的区别又在哪里呢?不同之处就在于getch()无返回显示,getche()有返回显示。就这么一点看看下面的例子:
[cpp] view plain copy
#include<stdio.h>
#include<conio.h>
void main()
{
char ch;
for(int i=0;i<5;i++)
{
ch=getch();
printf("%c",ch);
}
}
首先这是个连续5次的循环来实现5次停顿,等待我们输入,我们编译并运行这个程序,假设我们分别输入abcde,屏幕上显示的结果是abcde,这个abcde并不是在ch=getch();中输出的,我们把printf("%c",ch);这行语句去掉,就会发现我们按5次任意键程序就结束了,但屏幕上什么都没有显示。
然后我们在把代码中的getch()换成getche()看看有什么不同,我们还是分别输入abcde,这时屏幕上显示的结果是aabbccddee,我们把printf("%c",ch);这行语句再去掉看看,显示的结果就是abcde了,说明程序在执行ch=getche();这条语句的时候就把我们输入的键返回显示在屏幕上,有无回显就是getch()和getche()的唯一区别。
[cpp] view plain copy
void main()
{
char c, ch;
c=getch(); /*从键盘上读入一个字符不回显送给字符变量c*/
putchar(c); /*输出该字符*/
ch=getche(); /*从键盘上带回显的读入一个字符送给字符变量ch*/
putchar(ch);
printf("/n/n");
}
有人会说,既然是C的函数库中的,那么就应该淘汰了,我们还研究它,还用它干嘛?但是我发现还是有用着它的地方,否则我也不会在这里说这么多来耽误大家的时间。我就举个例子吧,程序如下:
[cpp] view plain copy
#include<stdio.h>
#include<conio.h>
void main()
{
char ch='*';
while(ch=='*')
{
printf("/n按 * 继续循环,按其他键退出!");
ch=getch();
}
printf("/n退出程序!");
}
我们可以在这个循环体中添加我们想要的功能,程序中按*继续循环,其他任意键退出,而且利用getch()无回显的特性,我们不管按什么,都不会在屏幕上留下痕迹,使我们的界面达到美观效果。
getchar()是stdio.h中的库函数,它的作用是从stdin流中读入一个字符,也就是说,如果stdin有数据的话不用输入它就可以直接读取了。而getch()和getche()是conio.h中的库函数,它的作用是从键盘接收字符。getchar带有显示。
与前面两个函数的区别在于: getchar()函数等待输入直到按回车才结束(前提是缓冲区没有数据),回车前的所有输入字符都会逐个显示在屏幕上。但只有第一个字符作为函数的返回值。
[cpp] view plain copy
#include<stdio.h>
#include<conio.h>
void main()
{
char c;
c=getchar(); /*从键盘读入字符直到回车结束*/
//getchar()在这里它只返回你输入字符串的第一个字符,并把返回值赋值给c
putchar(c); /*显示输入的第一个字符*/
printf("/n/n");
}
这个程序你运行一下,相信你又会有疑问了。这个就是从缓冲区中读取了例子。第一次getchar()时,确实需要人工的输入,但是如果你输了多个字符,以后的getchar()再执行时就会直接从缓冲区中读取了。
[cpp] view plain copy
#include<stdio.h>
#include<conio.h>
void main()
{
char c;
while ((c=getchar())!='/n') /*每个getchar()依次读入一个字符*/
printf("%c",c); /*按照原样输出*/
printf("/n/n");
}
程序运行时,首先停下来,等你输入一串字符串,输入完毕后,它把你输入的整个字符串都输出来了,咦,你不是说getchar()只返回第一个字符么,这里怎么?
因为我们输入的字符串并不是取了第一个字符就把剩下的字符串丢掉了,它还在我们的内存中,就好比,开闸放水,我们把水放到闸里去以后,开一次闸就放掉一点,开一次就放掉一点,直到放光了为止,这里开闸动作就相当于调用一次getchar()。我们输入的字符串也是这么一回事,首先我们输入的字符串是放在内存的缓冲区中的,我们调用一次getchar()就把缓冲区中里出口最近的一个字符输出,也就是最前面的一个字符输出,输出后,就把它释放掉了,但后面还有字符串,所以我们就用循环把最前面的一个字符一个个的在内存中释放掉,直到不满足循环条件退出为止。
例子中循环条件里的’/n’实际上就是你输入字符串后的回车符,所以意思就是说,直到遇到回车符才结束循环,而getchar()函数就是等待输入(或缓冲区中的数据)直到按回车才结束,所以实现了整个字符串的输出。当然,我们也可以把循环条件改一下,比如while ((c=getchar())!=‘a’),什么意思呢,意思就是遇到字符’a’就停止循环,当然意思是如果你输入“12345a213123/n”那么只会输出到a,结果是12345a。
再次注意:用getchar()它是从“流”中间去读取,所以第一个getchar()接受的是刚刚中断的流队列中即将出列的第一个字符(不限于回车符,上面举过例子了),如果流队列不为空,执行getchar()就继续放水,直到把回车符也放空为止,空了之后再在执行getchar()就停下等待你的输入了;我们用getch()为什么每次都是等待用户的输入呢?因为getch()是从键盘接收,即时的接收,并不是从stdin流中去读取数据。
补充:按键盘上的回车产生了2个字符:回车符(’/r’)和换行符(’/n’)。回车符’/r’(CR:carriage return:倒车)使光标回到这行的首部,换行符(’/n’)(new line)然后再换行。
所以当输入字符’w’,并按下回车键以后。首先得到回车符。那个getchar函数结束了。 但是还存在一个换行符。所以如果用getchar()来做判断的时候。最好再写一次getchar()清除缓冲区的’/n’.
如何清空输入缓冲区的内容?
如果我想让getchar()每次都能够等待用户输入的话就要清空缓冲区,下面就介绍方法(不同平台)
C标准规定 fflush()函数是用来刷新输出(stdout)缓存的。对于输入(stdin),它是没有定义的。但是有些编译器也定义了 fflush( stdin )的实现,比如微软的VC。其它编译器是否也定义了 fflush( stdin )的实现应当查找它的手册。GCC编译器没有定义它的实现,所以不能使用 fflush( stdin )来刷新输入缓存。
对于没有定义 fflush( stdin )的编译器,可以使用 fgets()函数来代替它(比用 getchar()、scanf()等函数通用性好)。可以这样忽略输入流中留下的回车等其它输入,从而使下一次的输入总保持一个“干净”的状态。(这个是任何平台下都可以的)
[cpp] view plain copy
// ...
char sbuf[1024];
// ...
fgets( sbuf, 1024, stdin );
// ...
//在windows 的vc下面就可以这样了:
for(int i=0;i<10;++i)
{
char ch=getchar();
fflush(stdin); //每次都会有等待状态了
}
总结 getch()无回显,getche()有回显,getchar()是读取流,而且和前面两个函数不是一个库。掌握清空缓冲区的方法。
scanf(), getchar()等都是标准输入函数,一般人都会觉得这几个函数非常简单,没什么特殊的。但是有时候却就是因为使用这些函数除了问题,却找不出其中的原因。下面先看一个很简单的程序:
[cpp] view plain copy
#include <stdio.h>
int main()
{
char ch1, ch2;
scanf("%c", &ch1);
scanf("%c", &ch2);
printf("%d %d/n", ch1, ch2);
return 0;
}
或者是:
[cpp] view plain copy
#include <stdio.h>
int main()
{
char ch1, ch2;
ch1 = getchar();
ch2 = getchar();
printf("%d %d/n", ch1, ch2);
return 0;
}
程序的本意很简单,就是从键盘读入两个字符,然后打印出这两个字符的ASCII码值。可是执行程序后会发现除了问题:当从键盘输入一个字符后,就打印出了结果,根本就没有输入第二个字符程序就结束了。
例如用户输入字符’a’, 打印结果是97,10。这是为什么呢?
【分析】:
首先我们呢看一下输入操作的原理, 程序的输入都建有一个缓冲区,即输入缓冲区。一次输入过程是这样的,当一次键盘输入结束时会将输入的数据存入输入缓冲区,而cin函数直接从输入缓冲区中取数据。正因为cin函数是直接从缓冲区取数据的,所以有时候当缓冲区中有残留数据时,cin函数会直接取得这些残留数据而不会请求键盘输入,这就是例子中为什么会出现输入语句失效的原因!
其实这里的10恰好是回车符!这是因为scanf()和getchar()函数是从输入流缓冲区中读取值的,而并非从键盘(也就是终端)缓冲区读取。而读取时遇到回车(/n)而结束的,这个/n会一起读入输入流缓冲区的,所以第一次接受输入时取走字符后会留下字符/n,这样第二次的读入函数直接从缓冲区中把/n取走了,显然读取成功了,所以不会再从终端读取!这就是为什么这个程序只执行了一次输入操作就结束的原因!
我们再看一下scanf()读取字符串的问题:
[cpp] view plain copy
#include <stdio.h>
int main()
{
char str1[20], str2[20];
scanf("%s",str1);
printf("%s/n",str1);
scanf("%s",str2);
printf("%s/n",str2);
return 0;
}
程序的功能是读入一个字符串输出,在读入一个字符串输出。可我们会发现输入的字符串中不能出现空格,例如:
测试一输入:
Hello world!
输出:
Hello
world!
【分析】到此程序执行完毕,不会执行第二次的读取操作!这个问题的原因跟问题一类似,第一次输入Hello world!后,字符串Hello world!都会被读到输入缓冲区中,而scanf()函数取数据是遇到回车、空格、TAB就会停止,也就是第一个scanf()会取出"Hello",而"world!"还在缓冲区中,这样第二个scanf会直接取出这些数据,而不会等待从终端输入。
测试二:
Hello[Enter]
Hello[输出]
world[Enter]
world[输出]
【分析】程序执行了两次从键盘读入字符串,说明第一次输入结束时的回车符被丢弃!即:scanf()读取字符串会舍弃最后的回车符!
我们再看一下gets()读取字符串的情况:
用scanf来读取一个字符串时,字符串中是不可以出现空格的,一旦出现空格,后面的数据就会舍弃残留在缓冲区中。其实有另外一个函数是可以接受空格的,那就是gets(),下面我们看一下这个函数的应用:
[cpp] view plain copy
#include <stdio.h>
int main()
{
char str1[20], str2[20];
gets(str1);
printf("%s/n",str1);
gets(str2);
printf("%s/n",str2);
return 0;
}
测试:
Hello world! [输入]
Hello world! [输出]
12345 [输入]
12345 [输出]
【分析】显然与上一个程序的执行情况不同,这次程序执行了两次从键盘的读入,而且第一个字符串取了Hello world! 接受了空格符,而没有像上一个程序那样分成了两个字符串!所以如果要读入一个带空格符的字符串时因该用gets(), 而不宜用scanf()!
小结:
第一:要注意不同的函数是否接受空格符、是否舍弃最后的回车符的问题!
读取字符时:
scanf()以Space、Enter、Tab结束一次输入,不会舍弃最后的回车符(即回车符会残留在缓冲区中);
getchar()以Enter结束输入,也不会舍弃最后的回车符;
读取字符串时:
scanf()以Space、Enter、Tab结束一次输入
gets()以Enter结束输入(空格不结束),接受空格,会舍弃最后的回车符!
第二:为了避免出现上述问题,必须要清空缓冲区的残留数据,可以用以下的方法解决:
方法1:**C语言里提供了函数清空缓冲区,**只要在读数据之前先清空缓冲区就没问题了!
这个函数是fflush(stdin)。
方法2:自己取出缓冲区里的残留数据。
scanf("%[^/n]",string); 扫描表为回车字符,读取时候遇到回车就停止!
getchar()函数只有在满足输入次数要求的前提下,遇到回车时才结束输入,之前所有的字符都会逐个显示在屏幕上,但是只有第一个字符返回一个ASCII码。
具体的过程就是getchar()被调用时,程序就等着用户按键,且将用户输入的字符依次回显到屏幕,用户必须摁完规定的次数(循环语句)(即使是回车也要摁),所有的字符都会保留在键盘缓存区中(回车字符也放在缓存区中),等待后续getchar调用读取。也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到读完规定的次数后(并不是读完缓冲区的字符),才等待用户按键。getch()函数任何时候遇到回车键都能结束,而且不会回显输入的字符,但是每输入一个字符都会立即返回一个ASCII码。具体的过程就是,每摁一次键就调用一次getch(),直到遇回车结束。如果在规定次数前结束,系统论并没有存入结束符,我们要手动地在他末尾添加一个‘/0’字符。这个也好理解,其实getchar()就相当于是翻译型的,他连回车字符也给翻译了,然后一起存入;而getch()是解释型的,每输入一条就翻译,然后存入,遇回车字符时刚翻译完是回车,所以他就得退出了。
[cpp] view plain copy
#include <stdio.h>
#include <conio.h>
//#include <iostream>
//using namespace std;
int main()
{
char tmp[20];
for ( int i=0 ; i<20; i++ )
{
tmp[i] = getch() ;
//tmp[i]=getchar();
// putchar ( '*' ) ;
printf ( "%d=[%c]/n",i,tmp [i] ) ;
//cout<<i<<"=["<<tmp [i]<<"]"<<endl;
if ( tmp[i] == '/r' )
{
tmp [i] = '/0' ;
break ;
}
}
}
上面这段代码,实现一个最多为20位的密码输入功能,既然是密码我们当然不希望他显示原字符,像linux用户验证那样什么也不显示,但有的时候为了直观一点,我们可以加入putchar(‘’)语句,就像QQ密码在你每输入一个字符时,他就回显一个“”号。
实例中的for循环了20次,但我们在输入的时候可以输入30,50个字符,对系统来说反正是放入缓冲区,放多少都是放,只不过在读取的时候只循环了20次,所以只能多20个字符,剩下的就被抛弃了。但是如果我们只想输入10个字符,那么你就得摁剩下的10个回车键,因为getchar()输入的字符被存放在键盘缓冲区中.直到用户输入足够的次数(20次)按回车后才返回。因为在20次前,回车也是当做一个普通字符存入缓存区的。
也就是说,如果你的按键超过20次getch()自动结束,而getchar()没有任何提示,但在下次调用时只取前20个有效字符。如果你的按键不足20次,getch()遇到回车就退出,但是getchar()你必须按足20次后遇回车才结束,
细心的朋友会发现,调用getchar()时真正输入的字符只有19个,因为系统默认把第一个字符设为‘/0’??这个我没发现!!
输入123回车
用getch时候输出为:
0=[0]
1=[1]
2=[2]
3=[3]
]=[
请按任意键继续. . .
关于]=[这一行,回车转为了/r/n按照转义字符处理了!用cout一样的结果!
getchar时候输出为:
123
0=[1]
1=[2]
2=[3]
3=[
]
456
4=[4]
5=[5]
6=[6]
7=[
]
关于3=[
] 看出直接转为了回车输出!
并没有结束,等到20次循环结束后才退出!