问题
有时我们在程序中使用EOF来结束输入,在linux系统下是CTRL+D,当我们按下该快捷键后代表我们将不再输入,但EOF也可能会给你的程序带来一些问题:
while(1)
{
int index;
scanf("%d", &index);
if(index == -1)
{
break;
}
printf("index = %d\n", index);
}
当该程序运行时按下CTRL+D会无线循环打印最后一次输入的index的值。你可以尝试一下C++是否会发生这种情况。
问题分析
经过测试C语言的scanf,getchar,gets,fgets,C++的cin都会发生上面的问题,EOF到底是什么?如何才能避免EOF所造成的无限循环呢?
EOF与CTRL+C不同,CTRL+C是一个信号,我们可以用屏蔽该信号的方法来屏蔽CTRL+D对程序的影响,但CTRL+D产生的EOF并不是信号,它表示数据流的结束。所以我们无法使用屏蔽CTRL+D的方式来解决该问题。
解决方法
方法1:
如果解决不了问题就解决出问题的人,有一个很有趣的方法就是将产生EOF的快捷键从CTRL+D改成其他的,这样CTRL+D就不会造成死循环了,但从更本上是无法解决EOF造成的死循环,下面的方法是交换两个快捷键,比如将CTRL+D和CTRL+C进行交换。如果仅仅是交换了两个键,有可能还是会产生EOF而造成死循环,方法2才是最保险的解决方法。
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
void sig_hnd(int sig)
{
(void)sig;
printf("CTRL+C");
}
int main()
{
setvbuf(stdout,NULL,_IONBF,0);
struct termios old_termios, new_termios;
tcgetattr(0,&old_termios);
new_termios = old_termios;
new_termios.c_cc[VEOF] = 3; // ^C
new_termios.c_cc[VINTR] = 4; // ^D
tcsetattr(0,TCSANOW,&new_termios); //修改终端设置
signal( SIGINT, sig_hnd );
char line[256]; int len;
while(1)
{
int index;
scanf("%d", &index);
if(index == -1)
{
break;
}
printf("index = %d\n", index);
}
tcsetattr(0,TCSANOW,&old_termios); //恢复终端设置
}
注意:一定要运行最后一行代码恢复原来的快捷键,不然你的终端的快捷键会被修改,造成一些麻烦。
方法2:
经过测试使用read从标准输入读取数据是不会被EOF阻断以后的输入的,如下代码
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <signal.h>
int main()
{
char line[256]; int len;
do
{
len=read(0,line,20);
line[len]='\0';
if( len <0 ) printf("(len: %i)",len);
if( len==0 ) exit(1);
if( len >0 )
{
if( line[len-1] == 10 ) printf("(line:'%.*s')\n",len-1,line); //未超出界限,直接输出
if( line[len-1] != 10 ) printf("(partial line:'%s')\n",line); //超出界限,分段输出
}
}while( line[0] != 'q' );
return 0;
}