2009-10-21 5 views
1

Этот код работает по своему усмотрению в большинстве случаев, что должно побуждать пользователя к одному символу, выполнять связанное действие, предлагать пользователю нажать return и повторить. Однако, когда я ввожу^D (EOF) в приглашении, возникает бесконечный цикл. Я очищаю состояние ошибки через std :: cin.clear() и вызываю std :: cin.ignore (...), чтобы очистить буфер. Что может вызвать бесконечный цикл?Бесконечный цикл на EOF в C++

#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, если это имеет значение.


UPDATE: То, что я действительно спрашиваю здесь, , когда пользователь вводит EOF (^ D) в командной строке, как я (а) обнаружить и (б) сброс потока так что пользователь может продолжать вводить данные.

Следующий пример отличается от кода выше, но иллюстрирует тот же принцип очистки потока после того, как^D был обнаружен и продолжает читать из этого потока.

> a 
you entered: a 
> b 
you entered: b 
> ^D 
you entered EOF 
> c 
you entered: c 
...
+0

Вы уверены, что это замкнутая петля? Пока нет - для заявлений в вашем коде. Вы отлаживали это? – Tom

+0

спасибо, Том. У меня есть цикл в моем коде, но я забыл включить его в этот фрагмент. – titaniumdecoy

+1

while (true) вызывает бесконечный цикл. – okutane

ответ

3

Вы всегда должны проверить, установлены ли какие-либо флаги отказа потока после вызова форматированной операции извлечения, в вашем примере вы проверяете response, не проверяя, правильно ли был извлечен response.

Кроме того, вы используете std::endl в своем приглашении, где это не имеет смысла. std::endl печатает \n, а затем сбрасывает буфер, но затем вы сразу же печатаете больше символов, чтобы флеш был лишним. Как cin и cout (обычно) связаны, вызов функции входа для std::cin заставит std::cout быть сброшены в любом случае, так что вы можете также поместить \n в вашу строку приглашения и сохранить на многословные дополнительных << операторов.

Почему бы не сделать функцию запроса, которая выводит запрос, извлекает ввод, возвращает ссылку на поток, чтобы вы могли проверить его на успех, используя обычный поток для преобразования типа boolean.

Таким образом, вы можете избавиться от истинного и явного перерыва.

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(); 
    } 
} 
+0

Спасибо за ваш ответ, но у вашего кода такая же проблема, как у меня. Мне нужно ПРОДОЛЖИТЬ ПУТЬ после того, как пользователь вводит EOF (^ D). Как только я помещаю prompt_for_input внутри цикла, который не контролируется его возвращаемым значением (например, while (true)), я получаю бесконечный цикл. – titaniumdecoy

+0

В принципе, что я хочу делать (и я понимаю, что это не то, что я написал, но он требует такой же функции), это прочитать список имен, введенных пользователем, по одному на каждую строку, за которым следует EOF (^ D) , Затем программе необходимо ПРОДОЛЖИТЬ ВЫПОЛНЕНИЕ (например, чтение из std :: cin), чтобы пользователь мог ответить на дополнительные запросы. – titaniumdecoy

+0

Попытка перезапустить поток после того, как EOF просто поможет вам получить точно такую ​​же проблему. Если поток действительно заканчивается, вы не сможете ничего что-либо прочитать. Сброс 'eofbit' и перенос выполняется только в очень ограниченных условиях. Вы говорите, что вход должен быть «один на линию», поэтому зачем вам ждать EOF? –

0

Вам необходимо очистить флажки, чтобы поток мог выполнять большую часть всего, что связано с EOF.

+0

Я считаю, что это должен сделать вызов std :: cin.clear() ... – titaniumdecoy

0

Err, мне может быть что-то не хватает, но я никогда не видел вас break из цикла while (true).

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

Этот код не отображается, так как он является частью() оператора switch. Я хочу постоянно читать одиночные символы, за которым следует возврат каретки (навсегда). – titaniumdecoy

+0

Я изменил код, чтобы сделать это более понятным. – titaniumdecoy

0

После прочтения EOF, вы просто игнорировать его и цикл обратно, не выходя из петли, так что вы будете постоянно читать EOF и постоянно цикл. Если вы хотите что-то сделать, увидев EOF, вам нужно обработать его либо в вашем коммутаторе, либо раньше.

Возможно, вы хотите прочитать ввод откуда-то после того, как пользователь закрыл ваш stdin с помощью^D? В этом случае вам придется закрыть cin и снова открыть его для чтения с другого места, которое вы хотите прочитать.

0

Как уже упоминалось, вам необходимо убедиться, что поток не находится в плохом состоянии. Я бы изменил условие на использование good(). Не просто проверяйте EOF, поскольку есть несколько способов, которыми поток может стать «плохим», кроме EOF.

while (std::cin.good()) {... 
+0

Но 'isgood' не является членом функции' std :: cin'? –

+0

Неверный API. Это просто называется good() для потоков C++. – jmucchiello

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'); 

}

2

вопрос не имеет смысла для стандартного ввода. После того, как поток закончится, будет сложно прочитать что-то со стандартного ввода - вам придется каким-то образом его открыть, но нет возможности повторно открыть стандартный ввод. Он может быть подключен к трубе, или к файлу, или к терминалу - и нет никакого поведения, подходящего для всех этих.

Значит, вы будете явно читать из терминала, я полагаю. В системах UN * X это означает чтение/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) 
}