如何彻底解决scanf输入流问题

Nou*_*eno 2 c standards user-input input scanf

假设我想运行以下 C 代码片段:

scanf("%d" , &some_variable);
printf("something something\n\n");

printf("Press [enter] to continue...")
getchar(); //placed to give the user some time to read the something something
Run Code Online (Sandbox Code Playgroud)

这个片段不会暂停!问题是 会将scanf“enter”( \n)字符留在输入流1中,弄乱其后面的所有内容;在这种情况下,他们getchar()会吃掉\n而不是等待真正的新角色。

由于我被告知不要使用fflush(stdin)(我真的不明白为什么),我能想出的最佳解决方案就是在代码开头重新定义扫描函数:


void nsis(int *pointer){ //nsis arconim of: no shenanigans integer scanf
     scanf("%d" , pointer);
     getchar(); //this will clean the inputstream every time the scan function is called
}
Run Code Online (Sandbox Code Playgroud)

然后我们只需使用nsis代替scanf. 这应该会飞。然而,这似乎是一个真正自制的、用胶带组合在一起的解决方案。专业的 C 开发人员如何处理这个烂摊子?scanf他们根本不使用吗?他们是否只是接受使用脏输入流?这里的标准是什么?

我无法在任何地方找到明确的答案!我能找到的每个来源都提到了不同的(且粗略的)解决方案......


编辑:回应所有评论某些版本的“只是不要使用 scanf”:好的,我可以做到这一点,但是scanf这样做的目的是什么?它只是一个不应该使用的无用的损坏功能吗?为什么它一开始就在图书馆里呢?

这看起来真的很荒谬,特别是考虑到所有初学者都被教导使用scanf......


[1]:左边\n的是用户输入变量值时输入的值some_variable,而不是出现在 中的值printf

Ste*_*mit 5

\n

但 scanf 的目的是什么?

\n
\n

这是一个很好的问题。

\n
\n

它只是一个不应该使用的无用的损坏功能吗?

\n
\n

这几乎毫无用处。可以说,它已经完全崩溃了。它几乎不应该被使用。

\n
\n

为什么它一开始就在图书馆里呢?

\n
\n

我个人认为这是一个实验。它试图与printf. 但事实证明,这在实践中并不是一个好主意,而且该函数从未被广泛使用,并且几乎不再受欢迎,除了一个特定的用例......

\n
\n

这看起来真的很荒谬,特别是考虑到所有初学者都被教导使用 scanf...

\n
\n

你是绝对正确的。这实在是太荒唐了。

\n

不过,为什么所有初学者都被教导使用 ,这是有充分理由的scanf。在你的第一堂 C 编程课的第一周,你可能会编写这个小程序

\n
#include <stdio.h>\n\nint main()\n{\n    int size = 5;\n    for(int i = 0; i < size; i++) {\n        for(int j = 0; j < size; j++)\n            putchar(\'*\');\n        putchar(\'\\n\');\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

打印一个正方形。在第一周,要制作不同大小的正方形,您只需编辑该行int size = 5;并重新编译即可。

\n

但很快 \xe2\x80\x94 说,在第 2 周 \xe2\x80\x94 中,您希望用户能够输入正方形的大小,而无需重新编译。你可能还没准备好乱搞argv. 您可能还没有准备好使用 读取一行文本fgets并将其转换回整数atoi。(您可能根本没有准备好认真考虑整数5和字符串之间的巨大差异"5"。)因此,在您的第一个 C 编程课程的第 2 周, \xe2\x80\x94scanf看起来就像是门票。

\n

这就是我所说的“一个特定用例”。如果您scanf在第一堂 C 编程课的第二周期间仅将小整数读入简单的 C 程序,那么事情不会那么糟糕。(你仍然会遇到忘记 的问题&,但这或多或少是可以管理的。)

\n

问题(尽管这又是我个人的信念)是它并不止于此。几乎每个初级 C 课程的讲师都会教学生使用scanf. 不幸的是,很少或根本没有这些老师明确告诉学生这scanf是一个权宜之计,可以在第二周暂时使用,并在接下来的几周内重点毕业。而且,更糟糕的是,许多教师继续布置更高级的问题,涉及scanf,这绝对不是一个好的解决方案,例如尝试进行稳健或“用户友好”的输入验证。

\n

scanf\ 唯一的优点是,它似乎是一种很好的、​​简单的方法,可以将用户的小整数和其他简单输入输入到早期程序中。但问题 \xe2\x80\x94 实际上是一大堆令人颤抖的 17 个独立问题 \xe2\x80\x94 ,事实scanf证明它非常复杂,充满异常且难以使用,与您的问题恰恰相反。 d 希望让初学者变得容易。 scanf只对初学者有用,对初学者几乎完全没用。它被描述为就像儿童自行车上的方形辅助轮。

\n
\n

专业的 C 开发人员如何处理这个烂摊子?

\n
\n

scanf很简单:根本不使用。一方面,很少有生产 C 程序将提示打印到基于行的屏幕上,并要求用户键入一些内容,然后按 Return 键。对于那些确实以这种方式工作的程序,专业的 C 开发人员会毫不犹豫地使用fgets或类似的方法将整行输入读取为文本,然后使用其他技术分解该行以提取必要的信息。

\n
\n

在回答你最初的问题时,没有好的答案。基本的使用规则之一scanf(顺便说一句,没有讲师教过的一组规则)是您永远不应该尝试在同一个程序中混合使用scanfgetchar(或)。fgets如果有一种好的方法可以让您的"Press [enter] to continue..."代码在调用后正常工作scanf,我们就不需要该规则。

\n

如果您确实想尝试刷新额外的换行符,以便稍后的调用getchar可以工作,这里有几个问题和一堆很好的答案:

\n\n
\n

还有一个不相关的点最终对您的问题非常重要。C 发明时,还没有多窗口 GUI 之类的东西。因此,没有一个 C 程序员会遇到输出在读取之前就消失的问题。因此,没有 C 程序员觉得有必要printf("Press [enter] to continue...");在后面编写getchar(). 我相信(另一个个人信念),对于任何基于 GUI 的 C 编译器的供应商来说,进行一些修改以使输出在程序退出时消失都是极其糟糕的行为。为了初级 C 程序员的利益,持久输出窗口应该是默认值,并使用某种非默认选项来为那些不想要它的人关闭该行为。

\n

  • *没有一个 C 程序员遇到过他们的输出在他们能够读取之前就消失的问题* 事实上,在 C 的早期,甚​​至没有视频终端,程序员使用的是打印在纸上的电传打字机,因此即使断电后您仍然可以读取输出! (2认同)
  • @Noumeno 另一个好问题。当然,很多人*已经*编写了各种“一体化”函数来执行此操作,所以您的问题实际上是,为什么这样的函数没有添加到官方 C 标准中?我认为答案分为两部分:(1) C 标准非常保守,实际上没有添加太多从一开始就没有的内容,(2) 很抱歉地说,确实没有迫切的需求,因为简单的输入函数实际上只有正在学习的初级程序员才需要,而 C 从来就没有打算作为教学语言。 (2认同)