C++中的EOF导致无限循环

3

这段代码大部分情况下是按预期工作的,即提示用户输入单个字符,执行相关操作,提示用户按回车键并重复。然而,当我在提示符处输入^D(EOF)时,会发生无限循环。我通过std::cin.clear()清除了错误状态,并调用std::cin.ignore(...)来清除缓冲区。可能是什么原因导致了无限循环?

#include <iostream>
#include <limits>

void wait()
{
    std::cout << std::endl << "press enter to continue.";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cin.clear();
    std::cin.get();
}

int main()
{
    char response;

    while (true)
    {
        std::cout << "enter a character at the prompt." << std::endl << "> ";
        std::cin >> response;
        switch (response)
        {
            case 'q':
                exit(0);
                break;
        }
        wait();
    }
}

我在Mac OS X终端中运行此程序,如果这很重要的话。
更新:我真正想问的是,在用户在提示符处输入EOF (^D)时,如何(a)检测它和(b)重置流,以便用户可以继续输入数据。 以下示例与上面的代码不同,但说明了在检测到^D后清除流并继续从该流读取的相同原理。
> a
您输入:a
> b
您输入:b
> ^D
您输入了EOF
> c
您输入:c
...

你确定这是一个无限循环吗?你的代码中没有while或for语句。你调试过了吗? - Tom
谢谢,Tom。我的代码中有一个循环,但是我忘记在这个片段中包含它了。 - titaniumdecoy
1
while(true)会导致无限循环。 - okutane
+1。你似乎已经修复了你的问题。 - mpen
7个回答

3
在调用格式化提取操作后,您应始终检查流的任何故障标志是否已设置,在您的示例中,您在未检查是否正确提取response的情况下检查了response
此外,您在不合适的地方使用std::endl进行提示输出。 std::endl打印\n,然后刷新缓冲区,但是您随后立即打印更多字符,因此刷新是多余的。由于cincout通常是关联的,因此调用std::cin的输入函数将导致std::cout在任何情况下都被刷新,因此您可以将\n放入提示字符串中并节省冗长的额外<<运算符。
为什么不创建一个提示函数,该函数打印提示,检索输入并返回对流的引用,以便您可以使用通常的流到布尔类型转换来测试它的成功。
这样,您就可以摆脱无限循环和显式断点。
std::istream& prompt_for_input( std::istream& in, std::ostream& out, char& response )
{
    out << "enter a character at the prompt.\n> ";
    in >> response;
    return in;
}

int main()
{
    char response;

    while ( prompt_for_input( std::cin, std::cout, response ) && response != 'q' )
    {
        wait();
    }
}

谢谢您的回答,但是您的代码和我的一样有问题。我需要在用户输入EOF (^D)之后继续循环。只要我将prompt_for_input放在不受其返回值控制的循环中(例如while(true)),我就会进入一个无限循环。 - titaniumdecoy
试图在EOF后重新启动流只会让你遇到完全相同的问题。如果流已经结束,你将无法从中读取任何内容。重置eofbit并继续操作只适用于非常有限的情况。你说输入应该是“每行一个”,那么为什么还需要等待EOF呢? - CB Bailey
你是在说一旦读取了EOF,就无法重置std::cin了吗?如果是这样,我能否通过另一个流或其他方式来路由std::cin? - titaniumdecoy
为什么不使用空行或单独一行的点作为信号呢?这样会更加便携。您已经排除了任何以 q 开头的行。 - CB Bailey
有一个我通过ssh使用的管理程序,它会提示输入,如果你输入^D,它只是重新显示提示符。这在标准C++中不可能吗? - titaniumdecoy
显示剩余5条评论

2
这个问题对于标准输入来说并没有意义。在标准输入流结束后,从中读取内容会很困难 - 你必须以某种方式重新打开它,但是没有办法重新打开标准输入。它可能连接到一个管道、文件或终端 - 但这些情况下都没有适当的行为。
所以我假设你要从终端明确地读取。在UNIX系统上,这意味着读取/dev/tty,并在需要时重新打开它。这里有一个简单的示例来演示如何实现;大多数错误检查被省略了。
// Warning: UN*X-specific

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    for(unsigned i=0; ; i++) {
        ifstream tty("/dev/tty");
        if (! tty) {
            cerr << "Failed to open TTY" << endl;
            return 2;
        }
        string s;
        while (getline(tty,s))
            cout << i << ": " << s << endl;
    }
    return 0;   // (unreached)
}

0

抱歉,我可能漏看了什么,但我从未看到您从while(true)循环中跳出

// ...
while (true) {
    if (std::cin.eof()) {
        break;
    }
    // ...
}

那段代码没有显示,因为它是空的 switch 语句的一部分。我想要不断地读取单个字符,后面跟着回车符(永远)。 - titaniumdecoy

0
如上所述,您需要确保流不处于错误状态。我会将while条件更改为使用good()。不要只检查EOF,因为除了EOF外,流可能出现“坏”情况的几种方式。
while (std::cin.good()) {...

0
while ((std::cout << "Enter a character at the prompt ")
      && (!(std::cin >> response) || response =='q')) {
 std::cout << "Not a valid input";
 std::cin.clear();
 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

}


0

在遇到 EOF 后,您需要清除标志才能使流执行任何操作。


0

当读取EOF时,只需忽略它并循环返回,而不退出循环,这样你将不断读取EOF并不断循环。如果您希望在看到EOF时执行某些操作,您需要在switch中或之前处理它。

也许你想在用户使用^D关闭stdin后从某个地方读取输入?在这种情况下,您必须关闭cin并重新打开它以从其他位置读取输入。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接