[C++] cin и getline

Автор osanve, 16 октября 2011, 18:49:37

« назад - далее »

0 Пользователи и 1 гость просматривают эту тему.

osanve

Здравствуйте.

Для начала приведу интересующий отрывок кода:
  ...
  cin >> n;
  for(int i = 0; i < n; i++)
  {
    string tmp;
    getline(cin, tmp);
    ...


А теперь собственно вопрос, т.к. я видимо чего-то не понимаю:
Почему на первой итерации цикла пропускается getline (или не пропускается, но записывается пустая строка) и сразу переходит на обработку введенного значения и как этого можно избежать?

Заранее спасибо.

malkavian

#1
Дело в том, что на первой итерации поток cin не пуст. Насколько я понимаю, там находится символ конца строки. Поэтому перед входом в цикл следует вызвать метод ignore, чтобы очистить входной поток. Вот простенький пример, чтобы проиллюстрировать сказанное.


// example.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
  short int n = 0;
  cout << "Enter a number, please:" << endl;
  cin >> n;
  cin.ignore();
  for(int i = 0; i < n; i++)
    {
      string tmp;
      getline(cin, tmp);
      cout << "Hello, " << tmp << endl;
    }
  cout << "Bye!" << endl;
  return 0;
}


Компилируем:


$ с++ example.cpp -o example


Вот пример работы:

$ ./example
Enter a number, please:
3
What is your name?
andrew
Hello, andrew
What is your name?
boris
Hello, boris
What is your name?
anna
Hello, anna
Bye!

osanve

malkavian, благодарю. Этого не знал.

tetramin

Если воспользоваться cin.clear(), результат будет тот же?

smallNix

#4
 Не совсем. У потока cin имеются так называемые биты состояний потока, которые характеризуют результат последней операции с потоком. К этим битам можно отнести:
- eofbit - устанавливается когда встречается символ EOF
- failbit - устанавливается при ошибке в потоке, но с возможностью восстановления данных
- badbit - устанавливается при ошибках с потерей данных
Имеется ещё goodbit - означает, что не один из указанных выше битов для последней операции не был установлен.
Функция clear позволяет явно установить некий бит (как правило применяется после обнаружения ошибки, для очистки - поведение по-умолчанию, т.е. без параметров), но можно, например, установить eofbit.
cin.clear (ios:eofbit);
Не очень представляю как использовать эту функцию для игнорирования символов потока ввода, а именно это и делает функция ignore
P.S.: Если вам надо определить результат успешности операции с потоком - можно воспользоваться методом rdstate:
unsigned char resState = cin.rdstate ();

Сообщение объединено: 30 марта 2012, 02:30:33

Решил добавить немного по теме. На самом деле malkavian немножко ошибся: при первой итерации символ перевода каретки '\n', до которого по-умолчанию и читает поток функция getline, не присутствует в потоке ввода. Ты сам его там оставляешь, если запустить программу

#include <iostream>
using std::cout;
using std::cin;
using std::endl;

int main (int argc, char* argv []) {
  short num = 3; // Три запуска
  const unsigned char SIZE = 100; // Можете через #define и помните про UNICODE
  char tmpBuf [SIZE]; // Или через указатель выделяйте - кому как нравится
  for (short i = 0; i < num; i++) {
   cout << "Попытка " << i << " из "<< num <<endl;
   cout << "Как тебя зовут? \n";

   cin.getline (tmpBuf, SIZE, '\n');
   cout << "\n Привет, "<<tmpBuf<<endl;
  }
  cout << "Пока!" << endl;
  return 0;
}


То все три попытки пройдут нормально, т.к. getline считывает строку и символ ограничитель. Последний удаляется из потока - проблем нет. Однако, ты хочешь, что бы количество итераций менялось, поэтому используешь просто cin, а не cin.getline - cin подобен cin.get - он читает данные до ограничителя, но не удаляет его - вот тебе и результат - в первом вызове программа читает оставшийся в наследство от ввода cin символ '\n' и мы получаем строку нулевой длины.
Выходов из этого много, но наименее корявые - или, как подсказал  malkavian  с помощью cin.ignore(1); пропусти символ, либо до первого вызова получай данные через getline.
Т.о. код, который вы, вроде, и так получили.

#include <iostream>
using std::cout;
using std::cin;
using std::endl;

int main (int argc, char* argv []) {
  short num = 0;
  const unsigned char SIZE = 100; // Можете через #define и помните про UNICODE
  char tmpBuf [SIZE]; // Или через указатель выделяйте - кому как нравится
  cout << "Введите количество попыток:" << endl;
  cin >> num; // Контроль корректности ввода опустим 
  cout << endl;
  cin.ignore(1);
  for (short i = 0; i < num; i++) {
   cout << "Попытка " << i << " из "<< num <<endl;
   cout << "Как тебя зовут? \n";

   cin.getline (tmpBuf, SIZE, '\n');
   cout << "\n Привет, "<<tmpBuf<<endl;
  }
  cout << "Пока!" << endl;
  return 0;
}


Надеюсь, что до варианта с вводом только через getline справишься самостоятельно =)
Кто-то же должен что-то делать...