Проблема использования Cin дважды

Вот код:

string str;
cin>>str;
cout<<"first input:"<<str<<endl;
getline(cin, str);
cout<<"line input:"<<str<<endl;

В результате getline никогда не приостанавливается для пользовательского ввода, поэтому второй вывод всегда пуст.

Потратив некоторое время на это, я понял, что после первого вызова "cin>>str" кажется, что \n все еще сохраняется в cin (с помощью cin.peek() для проверки), который немедленно завершает getline. Решением будет добавление еще одной строки между первым использованием и вторым:cin.ignore(numeric_limits::max(), '\n');

Тем не менее, я до сих пор не понимаю, почему после первого звонка остается "\n"? Что на самом деле делает istream& operator>>?

6 ответов

Решение

Хорошая рецензия, объясняющая некоторые причины, по которым вы сталкиваетесь с этой проблемой, в первую очередь из-за поведения типов ввода и их смешивания.

\n остается во входном потоке согласно пути operator>> определяется для std::string, std::string заполняется символами из входного потока, пока не будет найден символ пробела (в этом случае \n), после чего заполнение прекращается, и пробел становится следующим символом во входном потоке.

Вы также можете удалить \n позвонив cin.get() незамедлительно после cin>>str, Однако существует много, много разных способов снятия шкур с этого конкретного кота ввода / вывода. (Возможно, хороший вопрос сам по себе?)

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

Как уже говорили другие, проблема заключается в том, что символ новой строки оставлен после первого извлечения. Одним из решений, которое я делаю, является удаление всех оставшихся символов в потоке:

std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );

Я обычно рекомендую делать только строковый ввод из std::cin. Итак, ваш код может выглядеть примерно так:

string str;
int val;
// Read an entire line and parse an integer from it
{
    string line;
    getline(cin, line);
    istringstream iss(line);
    iss >> val;
}
cout<<"first input:"<<val<<endl;
getline(cin, str);
cout<<"line input:"<<str<<endl;

Не забудьте также добавить проверку ошибок.

Подход только для getline позволяет избежать необходимости думать о буферизации строки ввода, об очистке ввода и т. Д. Если вы что-то читаете напрямую с помощью >>, ввод не завершается, если пользователь нажимает ввод вместо ввода того, что требуется, но вместо этого продолжается до тех пор, пока токен не будет введен (это поведение обычно не требуется).

Также искал наиболее подходящее решение. Реализация этого оператора может вызвать проблемы. И не всегда приемлемо читать всю строку или не смешивать разные типы в одной строке ввода.

Чтобы решить проблему, когда вы хотите прочитать некоторые данные из cinи не знаете, правильно ли были извлечены пробелы после последней операции ввода, вы можете сделать это так:

std::string str;
std::cin >> std::ws >> str;

Но вы не можете использовать это для очистки завершающего символа новой строки после последней операции ввода из cin, чтобы не повлиять на новый ввод, потому что std::ws будет использовать все пробелы и не вернет управление до тех пор, пока не появится первый не-ws символ или EOF будет найден, поэтому нажатие Enter не завершит процесс ввода. В этом случае следует использовать

 std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );

который является более гибким.

PS Если есть ошибки с max() такая функция, как "ожидаемый идентификатор", может быть вызвана max макросы, определенные в некотором заголовке (например, Microsoft); это можно исправить с помощью

 #undef max
Другие вопросы по тегам