每次输入后刷新标准输入 – 哪种方法不是错误的?

Mark Lakata指出在我的问题中没有正确定义垃圾之后我想出了这个。 我会保持更新,以避免混淆。


我试图获得一个函数,我可以在提示用户输入之前调用printf("Enter your choice:);然后跟踪scanf并确保只有提示后输入的内容才会被scanf扫描为有效输入。

据我所知,所需的function是完全冲洗标准输入的东西。 这就是我想要的。 因此,出于此function的目的, "garbage"是用户输入中的所有内容,即在该用户提示之前的整个用户输入。


在C中使用scanf() ,总是存在输入缓冲区中存在额外输入的问题。 所以我在寻找一个函数,我在每次scanf调用后调用它来解决这个问题。 我用这个 , 这个 , 这个和这个来得到这些答案

 //First approach scanf("%*[^\n]\n"); //2ndapproach scanf("%*[^\n]%*c"); //3rd approach int c; while((c = getchar()) != EOF) if (c == '\n') break; 

所有这三个都是我通过命中试验找到的工作,并通过参考。 但是在我的所有代码中使用任何这些代码之前,我想知道这些代码中是否有任何错误?

编辑:

感谢Mark Lakata的第3个错误。 我在问题中纠正了它。

EDIT2:

在Jerry Coffin回答之后,我在代码中使用该程序测试了前两种方法:使用GNU GCC编译器阻止IDE 12.11(版本未在编译器设置中声明)。

 #include int main() { int x = 3; //Some arbitrary value //1st one scanf("%*[^\n]\n"); scanf("%d", &x); printf("%d\n", x); x = 3; //2nd one scanf("%*[^\n]%*c"); scanf("%d", &x); printf("%d", x); } 

我使用了以下2个输入

第一次测试输入 (2个换行但在垃圾输入中间没有空格)

 abhabdjasxd 23 bbhvdahdbkajdnalkalkd 46 

对于第一个,我得到了printf语句的以下输出

 23 46 

即两个代码都正常工作。

第二个测试输入 :(2个在垃圾输入中间有空格的换行符)

 hahasjbas asasadlk 23 manbdjas sadjadja a 46 

对于第二个,我得到了printf语句的以下输出

 23 3 

因此我发现第二个不会处理额外的垃圾输入空格。 因此,它不是垃圾输入的万无一失。

我决定尝试第三个测试用例 (垃圾包括非空格字符前后的换行符)

 `` hahasjbas asasadlk 23 manbdjas sadjadja a 46 

答案是

 3 3 

即在这个测试案例中都失败了。

前两个略有不同:它们都读取并忽略所有字符直到新行。 然后第一个跳过所有连续的空格,因此在执行之后,您读取的下一个字符将是非空格。

第二个读取并忽略字符,直到它遇到一个新行,然后再读取(并丢弃)一个字符。

如果您有(例如)双倍行距文本,则会显示差异,例如:

 line 1 line 2 

让我们假设你读到了第1行中间的某个地方。如果你执行第一个,你读入的下一个字符将是第2行的’l’。如果你执行第二个字符,你读到的下一个字符将是是第1行和第2行之间的新行。

至于第三个,如果我要做这个,我会做类似的事情:

 int ch; while ((ch=getchar()) != EOF && ch != '\n') ; 

…是的,这确实可以正常工作 – &&强制一个序列点,所以首先评估它的左操作数。 然后有一个序列点。 然后,当且仅当左操作数计算为true ,它才会计算其右操作数。

至于性能差异:既然你开始处理I / O,那么所有这些都将永远受I / O约束的问题几乎没有。 尽管其显而易见的复杂性,但scanf (和公司)通常是经过多年使用并经过多年精心优化的代码。 在这种情况下,手动循环可能会慢得多(例如,如果getchar的代码没有内联扩展),或者它可能大约相同的速度。 如果编写标准库的人不称职,那么唯一可以显着提高速度的方法就是它。

至于可维护性:IMO,任何声称知道C的人都应该知道scanf的扫描集转换。 这既不是新的也不是火箭科学。 任何不了解它的人都不是一个称职的C程序员。

前两个例子使用了我甚至不知道存在的scanffunction,我相信很多其他人都不知道。 能够在将来支持某项function非常重要。 即使它是一个众所周知的function,它的效率也会比第3个示例更难以读取格式字符串。

第三个例子看起来很好。

(编辑历史:我犯了一个错误,说ANSI-C不保证从左到右评估&&并提出了改变。但是,ANSI-C确实保证了对&&的左右评价。我不确定关于K&R C,但我找不到任何参考,也没有人用它…)

许多其他解决方案存在的问题是,当没有任何内容可以刷新时,它们会导致程序挂起并等待输入。 等待EOF是错误的,因为在用户完全关闭输入之前你没有得到它!

在Linux上,以下将执行非阻塞刷新:

 // flush any data from the internal buffers fflush (stdin); // read any data from the kernel buffers char buffer[100]; while (-1 != recv (0, buffer, 100, MSG_DONTWAIT)) { } 

Linux手册页说, stdin上的fflush是非标准的,但“大多数其他实现与Linux的行为相同”。

MSG_DONTWAIT标志也是非标准的(如果没有要传送的数据,它会导致recv立即返回)。

你应该使用getline / getchar

 #include  int main() { int bytes_read; int nbytes = 100; char *my_string; puts ("Please enter a line of text."); /* These 2 lines are the heart of the program. */ my_string = (char *) malloc (nbytes + 1); bytes_read = getline (&my_string, &nbytes, stdin); if (bytes_read == -1) { puts ("ERROR!"); } else { puts ("You typed:"); puts (my_string); } return 0; 

我想如果你仔细看到这个页面的右侧,你会看到许多类似于你的问题。 您可以在Windows上使用fflush()。