Автор Тема: [C++] cin и getline  (Прочитано 14316 раз)

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

Оффлайн osanve

  • Пользователь
  • *
  • Topic Author
  • Сообщений: 10
  • Jabber: osanve@jabber.ru
[C++] cin и getline
« : 16 Октябрь 2011, 18:49:37 »
Здравствуйте.

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

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

Заранее спасибо.
« Последнее редактирование: 16 Октябрь 2011, 18:54:10 от osanve »
 

Оффлайн malkavian

  • Новичок форума
  • Сообщений: 4
Re: [C++] cin и getline
« Ответ #1 : 24 Октябрь 2011, 17:07:14 »
Дело в том, что на первой итерации поток 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!
« Последнее редактирование: 24 Октябрь 2011, 17:13:56 от malkavian »
 

Оффлайн osanve

  • Пользователь
  • *
  • Topic Author
  • Сообщений: 10
  • Jabber: osanve@jabber.ru
Re: [C++] cin и getline
« Ответ #2 : 25 Октябрь 2011, 04:56:23 »
malkavian, благодарю. Этого не знал.
 

Оффлайн tetramin

  • Пользователь
  • *
  • Сообщений: 17
  • Jabber: tetramin@jabber.ru
Re: [C++] cin и getline
« Ответ #3 : 08 Март 2012, 05:49:12 »
Если воспользоваться cin.clear(), результат будет тот же?
 

Оффлайн smallNix

Re: [C++] cin и getline
« Ответ #4 : 30 Март 2012, 01:23:46 »
 Не совсем. У потока 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 справишься самостоятельно =)
« Последнее редактирование: 30 Март 2012, 02:30:33 от smallNix »
Кто-то же должен что-то делать...
 

Теги: