Как использовать команду getline в c ++? - PullRequest
6 голосов
/ 03 февраля 2012

Я пытаюсь превратить команду cout в команду getline в c ++.

Это мой код, который я пытаюсь изменить ....

for (int count=0; count < numberOfEmployees; count++)
    {
        cout << "Name: ";
        cin >> employees[count].name; 

        cout << "Title: ";
        cin >> employees[count].title;

        cout << "SSNum: ";
        cin >> employees[count].SSNum;

        cout << "Salary: ";
        cin >> employees[count].Salary;

        cout << "Withholding Exemptions: ";
        cin >> employees[count].Withholding_Exemptions; 
    }

Я пытаюсь изменить эту строку: cin >> employees[count].name; и эту строку: cin >> employees[count].title; на getlines. Кто-нибудь может помочь?

Спасибо

Ответы [ 5 ]

9 голосов
/ 03 февраля 2012

Сброс проблем с cin.getline () в C ++

Когда вы хотите удалить посторонние символы из потока ввода в C ++, обычно это происходит потому, что вы смешали отформатированные и неформатированные методы ввода.Форматированный метод оставил бы новую строку в потоке, а неформатированный метод использовал бы ее и успешно завершил, но не смог бы полностью выполнить то, что хотел.

    #include <iostream>  
 int main() {   
std::cout<<"Enter the letter A: ";  
 std::cin.get();   
std::cout<<"Enter the letter B: "; 
  std::cin.get();   
std::cout<<"Too late, you can't type anymore\n";
 }

Часто этот вопрос возникает из другого вопроса, а именно:приостановить программу до ее завершения.Использование cin.get () работает только тогда, когда в потоке не осталось символов.В тот момент, когда вы бросаете монетку >> foo;в коде внезапно решение не удается.Вам нужно удалить все оставшиеся символы из потока, прежде чем он снова заработает.

Итак, как вы решаете проблему?Хорошей новостью является то, что если вы не хотите быть разборчивым, это так же просто, как цикл: C ++ Синтаксис (Toggle Plain Text)

    #include <istream>  
 void ignore_line ( std::istream& in ) { 
  char ch;
        while ( in.get ( ch ) && ch != '\n' );
 } 

Этот цикл просто читает символы до конца файла или новой строкичитать.Обычно предполагается, что интерактивный ввод в C ++ ориентирован на строки, и вы гарантированно получите чистый буфер после чтения новой строки.Хотя это не так (входные данные не обязательно должны быть ориентированы на строки), они достаточно широко распространены, чтобы их можно было использовать для целей этого потока.

Так что же такого в этом подходе?Ничего такого.На самом деле, это почти так же хорошо, как и получается, если только вы не хотите копать и исправлять тонкие проблемы.Но прежде чем мы рассмотрим проблемы, вот альтернативное решение, которое делает то же самое по-другому:

    #include <ios>
    #include <istream>
    #include <limits>   
void ignore_line ( std::istream& in ) {   
in.ignore ( std::numeric_limits<std::streamsize>::max(), '\n' );
 } 

Функция игнорирования члена std :: istream будет читать и отбрасывать до N символов или доразделитель.В приведенном выше примере N представлено наибольшим значением типа данных streamsize, а разделителем является символ новой строки.Он одинаково хорошо работает только с большим значением (обычно 80): Синтаксис C ++ (Toggle Plain Text) in.ignore (80, '\ n');Однако тип данных streamsize, скорее всего, будет точным представлением размера буфера, который использует поток, и он с большей вероятностью будет работать все время.Это решение, которое я рекомендую.

Так что с этим не так?Есть две заметные проблемы.Первое легко исправить, и оно связано с тем, что istream не очень гибок.istream на самом деле является typedef для basic_istream.Если вы хотите, чтобы широкий поток работал с ignore_line, вы SOL с istream.Таким образом, хитрость заключается в том, чтобы вместо этого использовать basic_istream <>:

    #include <ios>
    #include <istream>
    #include <limits>   
template <typename CharT> 
void ignore_line ( std::basic_istream<CharT>& in ) { 
  in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) ); 
}

Теперь ignore_line - это функция шаблона, которая будет извлекать тип символов, содержащийся в потоке, из первого аргумента.Вы можете передать любой поток, который является производным от basic_istream или специализируется на нем, и проблема исчезла.Вместо '\ n' рекомендуется использовать расширение для литерала, чтобы при необходимости он был должным образом преобразован в более широкий тип.Красиво и просто.

Вторая проблема сложнее.Гораздо труднее.Это сложнее, потому что стандартные iostreams, кажется, блокируют ваш путь на каждом шагу из-за отсутствия мобильности или нежелательных функций.На самом деле невозможно полностью решить проблему.Проблема в том, что поведение отличается в зависимости от содержимого потока.Например:

    #include <iostream>
    #include <ios>
    #include <istream>
    #include <limits>  
 template <typename CharT> 
void ignore_line ( std::basic_istream<CharT>& in ) { 
  in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );
 }  
 int main() {  
 std::cout<<"First input: "; 
  std::cin.get();  
 std::cout<<"Clearing cin.\n"; 
  std::cin.clear();   
ignore_line ( std::cin ); 
  std::cout<<"All done.\n"; 
} 

Запустите эту программу три раза:

Ввод: "asdf" Вывод: Программа завершается без ввода дополнительных данных.

Ввод: простонажмите Enter. Выход: программа ждет, когда вы нажмете Enter еще раз.

Ввод: Сигнал EOF Выход: Программа ждет, когда вы нажмете Enter еще раз.

Проблема в том,поток пустЕсли вы сразу нажмете Enter, новая строка будет помещена в поток и использована cin.get.Аналогично с сигнализацией EOF.В этот момент в потоке ничего не остается, и cin.ignore останавливает все, пока вы не наберете больше символов.Это потому, что cin.ignore является блокирующим чтением.Если нечего читать, он ждет.

То, что мы хотели бы сделать, это не блокировать ни для одного из этих трех случаев. Хорошей новостью является то, что библиотека iostream поддерживает некоторые возможные решения. Плохая новость в том, что это тупики. Вот два распространенных:

Функция-член sync. Класс istream поддерживает функцию-член, называемую sync. Почему у него такая функция, обсуждается, потому что никто не может договориться о том, что он должен делать. Даже сам Бьярне Страуструп неправильно заявил, что отбрасывает всех персонажей в потоке:

  #include <iostream>  
 int main() {   
std::cout<<"First input: ";  
 std::cin.get();   
std::cout<<"Clearing cin.\n";  
 std::cin.clear();   
std::cin.sync();   
std::cout<<"All done.\n"; 
} 

Когда это работает, это работает прекрасно. Плохая новость заключается в том, что стандарт C ++ не требует синхронизации для выполнения чего-либо, например, отбрасывания посторонних символов. Это решение непереносимо.

Функция-член in_avail. Следующим шагом является просмотр функции-члена in_avail в потоковом буфере istream. На первый взгляд кажется, что эта функция-член сообщит вам, сколько символов в потоке, и если она вернет 0, вы можете отказаться от вызова ignore:

  #include <iostream>
    #include <ios>
    #include <istream>
    #include <limits>  
 template <typename CharT> 
void ignore_line ( std::basic_istream<CharT>& in ) {
   if ( in.rdbuf()->in_avail() > 0 )
        in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );
 }  
 int main() { 
  std::cout<<"First input: "; 
  std::cin.get();   
std::cout<<"Clearing cin.\n"; 
  std::cin.clear();  
 ignore_line ( std::cin ); 
  std::cout<<"All done.\n";
 }

Как и в случае с синхронизацией, когда это работает, это прекрасно работает. Но еще раз, стандарт поднимает стену, говоря, что in_avail не требуется, чтобы дать вам точное представление символов в потоке. На самом деле, некоторые популярные реализации имеют строго соответствующий in_avail, который всегда возвращает 0. Не очень полезно. Теперь мы должны проявить творческий подход.

Функция-член возврата

 #include <iostream>
    #include <ios>
    #include <istream>
    #include <limits> 
  template <typename CharT>
 void ignore_line
 ( std::basic_istream<CharT>& in ) { 
  if ( !in.putback ( in.widen ( '\n' ) ) )
        in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );   else
        in.ignore(); }   
int main() 
{   std::cout<<"First input: ";   
std::cin.get();   
std::cout<<"Clearing cin.\n"; 
  std::cin.clear();  
 ignore_line ( std::cin );  
 std::cout<<"All done.\n";
 } 

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

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

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

  #include <iostream>
    #include <ios>
    #include <istream>
    #include <limits>  
 template <typename CharT>
 void ignore_line ( std::basic_istream<CharT>& in ) { 
  if ( in.rdbuf()->sungetc() != std::char_traits<CharT>::eof()
        && in.get() != in.widen ( '\n' ) )   {
        in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );  
 } 
}   
int main() {   
std::cout<<"First input: ";  
 std::cin.get();   
std::cout<<"Clearing cin.\n";   
std::cin.clear();   
ignore_line ( std::cin );   
std::cout<<"All done.\n";
 }

Причина, по которой мы используем sungetc вместо unt istream, заключается в том, что unget возвращает поток, а sungetc возвращает либо символ, который был сдвинут назад, либо EOF. Таким образом, мы можем определить, не сработала ли функция или нет.

В случае сбоя sungetc будет выполнено одно из следующих действий:

1) Поток находится в состоянии ошибки. 2) Нет символов, которые можно забыть. 3) Поток не поддерживает незабываемые символы.

Если sungetc преуспеет, всегда будет персонаж, который можно прочитать и проверить на символе новой строки. Если этот символ соответствует новой строке, то последний прочитанный символ также был новой строкой, и нам не нужно вызывать игнорировать. Если символ не совпадает, то полная строка не была прочитана, и мы можем безопасно вызвать игнорирование без блокировки.

Если поток находится в состоянии ошибки, это то, с чем должен обращаться вызывающий код. Если нет символов, которые можно было бы забыть, то это именно то, для чего предназначено это решение. Но, если поток не поддерживает незабываемые символы, это проблема. Функция ignore_line всегда будет не в состоянии отбрасывать символы, поэтому для тех реализаций, которые не поддерживают символы без символов, мы можем добавить флаг, который вызывает игнорирование. Иногда полезно знать, сколько символов было проигнорировано, поэтому давайте добавим это и у нас есть окончательное решение:

   #include <ios>
    #include <istream>
    #include <limits>   
template <typename CharT> 
std::streamsize ignore_line (   std::basic_istream<CharT>& in, bool always_discard = false ) { 
  std::streamsize nread = 0;
        if ( always_discard || ( in.rdbuf()->sungetc() != std::char_traits<CharT>::eof()
        && in.get() != in.widen ( '\n' ) ) )  
 {
        // The stream is good, and we haven't
        // read a full line yet, so clear it out
        in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );
        nread = in.gcount();   }
        return nread; 
}

Я просто включу манипулятор, который вызывает ignore_line, а также манипулятор, который использует ignore_line для приостановки программы. Таким образом, немытые массы могут прекратить использование системы («ПАУЗА»); и getch ();:

  class ignoreline { 
  bool _always_discard;
   mutable std::streamsize _nread; 
public:  
 ignoreline ( bool always_discard = false )
        : _always_discard ( always_discard ), _nread ( 0 )   {}
        std::streamsize gcount() const { return _nread;
 }
        template <typename CharT>  
 friend std::basic_istream<CharT>& operator>> (        std::basic_istream<CharT>& in, const ignoreline& manip )  
 {
        manip._nread = ignore_line ( in, manip._always_discard );
        return in;  
 } 
};  
 class pause { 
  ignoreline _ignore; 
public:   
pause ( bool always_discard = false )        : _ignore ( always_discard )   {}
        std::streamsize gcount() 
const { return _ignore.gcount(); 
}
        template <typename CharT> 
  friend std::basic_istream<CharT>& operator>> (        std::basic_istream<CharT>& in, const pause& manip )   
{
        if ( !( in>> manip._ignore ) )
          in.clear();

        std::cout<<"Press Enter to continue . . .";

        return in.ignore();  
 } 
}; 

Теперь все три случая ведут себя одинаково:

     int main() 
{   std::cout<<"First input: "; 
  std::cin.get();   
std::cout<<"Clearing cin.\n";  
 std::cin>> ignoreline();  
 std::cout<<"All done.\n";  
 std::cin>> pause();
 } 

И мораль этой истории такова: никогда не бывает так просто, как кажется, написание переносимого кода, который делает то, что вам нужно, чрезвычайно сложно, а библиотека iostream - огромный беспорядок.

ПРИМЕЧАНИЕ: Если вы новичок, забудьте все и просто поймите, что есть проблема с промывкой, и используйте cin

1 голос
/ 03 февраля 2012

Вы можете использовать функцию getline, которая принимает входной поток в качестве первого аргумента. Это прототип выглядит так: basic_istream<...>& getline(basic_istream<...>& _Istr, basic_string<...>& _Str)

Но тогда вам нужно подумать о том, что вы имеете дело с std::string, и проанализировать его как тип, который вам действительно нужен (char*, int, как угодно):

#include <iostream>
#include <sstream>
#include <string>
using namespace std;
//...

for (int count=0; count < numberOfEmployees; count++)
{
    string name, title, sSSnum, sSalary, s;

    cout << "Name: ";
    getline(cin, name); 
    employees[count].name = name.c_str();

    cout << "Title: ";
    getline(cin, title);
    employees[count].title = name.c_str();

    cout << "SSNum: ";
    getline(cin, sSSnum);
    istringstream is(sSSnum);
    is >> employees[count].SSNum;

    cout << "Salary: ";
    getline(cin, sSalary);
    is.clear();
    is.str(sSalary);
    is >> employees[count].Salary;
    //...
}
1 голос
/ 03 февраля 2012

Взглянув на прототип getline , вы увидите, что вам нужны две вещи:

  1. Объект istream
  2. Строка для результата
  3. (необязательно) разделитель "новая строка" (по умолчанию '\n'.

В вашем случае кажется, что разделитель может быть значением по умолчанию: '\n'; если нет, вы можете указать, какой символ отделяет каждое значение (возможно, это пробел ' ' или что-то) Поток cin будет вашим istream объектом, и каждое из свойств employees[count] будет служить вторым параметром. В итоге, getline(cin, employees[count].name); должен добиться цели.

0 голосов
/ 03 февраля 2012

istream & getline (char * s, streamsize n);istream & getline (char * s, streamsize n, char delim);Получить строку из потока

Извлекает символы из входной последовательности и сохраняет их в виде c-строки в массиве, начинающемся с s.

Символы извлекаются до тех пор, пока не будет (n - 1) символовизвлечен или найден символ-разделитель (который является разделителем, если указан этот параметр, или '\ n' в противном случае).Извлечение также останавливается, если достигнут конец файла во входной последовательности или если во время операции ввода возникает ошибка.

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

Конечный нулевой символ, сигнализирующий об окончании c-строки, автоматически добавляется к s после извлечения данных.

Количество символов, читаемых этой функцией, можно получить, вызвав функцию-член gcount.

В заголовке существует глобальная функция с тем же именем.Эта глобальная функция обеспечивает аналогичное поведение, но со стандартными строковыми объектами C ++ вместо c-строк: см. Getline (string).

Параметры s Указатель на массив символов, в котором строка хранится как c-строка.Максимальное количество символов для хранения (включая завершающий нулевой символ).Это целочисленное значение типа streamsize.Если функция прекращает чтение из-за достижения этого размера, устанавливается внутренний флаг failbit.delim Разделительный символ.Операция извлечения последовательных символов останавливается, когда этот символ читается.Этот параметр является необязательным, если он не указан, функция считает символ \ n (символ новой строки) символом-разделителем.

Возвращаемое значение Функция возвращает * this.

ПРИМЕР MSDN:

// istream getline
#include <iostream>
using namespace std;

int main () {
  char name[256], title[256];

  cout << "Enter your name: ";
  cin.getline (name,256);

  cout << "Enter your favourite movie: ";
  cin.getline (title,256);

  cout << name << "'s favourite movie is " << title;

  return 0;
}

А для вашей ситуации со структурным объектом вам нужно использовать objectname.datamember для доступа к данным объектов:

for (int count=0; count < numberOfEmployees; count++)
    {
        cout << "Name: ";
       cin.getline (employees[count].name,100); 

        cout << "Title: ";
        cin.getline (employees[count].title,100); 

        cout << "SSNum: ";
        cin.getline (employees[count].SSNum); 

        cout << "Salary: ";
        cin.getline( employees[count].Salary);

        cout << "Withholding Exemptions: ";
       cin.getline (employees[count].Withholding_Exemptions);
    }
0 голосов
/ 03 февраля 2012
for (int count=0; count < numberOfEmployees; count++)
    {
        cout << "Name: ";
       cin.getline (employees[count].name,100); 

        cout << "Title: ";
        cin.getline (employees[count].title,100); 

        cout << "SSNum: ";
       //error cin.getline (employees[count].SSNum); 
       cin>>employees[count].SSNum;

        cout << "Salary: ";
        // error cin.getline( employees[count].Salary);
        cin>>employees[count].Salary;


        cout << "Withholding Exemptions: ";
       //error cin.getline (employees[count].Withholding_Exemptions);
       cin>>employees[count]..Withholding_Exemptions;
    }
...