How to use the getline command in c++?
Asked Answered
W

5

6

I'm trying to turn a cout command into a getline command in c++.

This is my code that I'm trying to changes....

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; 
    }

I'm trying to change this line: cin >> employees[count].name; and this line: cin >> employees[count].title; into getlines. Can anyone help?

Thanks

Wearable answered 3/2, 2012 at 6:29 Comment(4)
getline(cin, employees[count].name)?Nonrepresentational
possible duplicate of Need help with getline() and #2040418 and #4067756 and #5740437Gaby
@R.MartinhoFernandes: That's not enough, the whitespace remaining after the previous input will terminate the line immediately. Let's close as dupe; this has been explained before.Gaby
@Ben Oh. I don't think I ever mixed line-based input with "regular" input, as I never ran into that. You're right, that's probably what's "not working" here.Nonrepresentational
G
10

Flushing Problems with cin.getline() in C++

When you want to remove extraneous characters from an input stream in C++, it's usually because you mixed formatted and unformatted input methods. The formatted method would leave a newline in the stream and the unformatted method would consume it and terminate successfully, but fail completely to do what you wanted.

    #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";
 }

Often this question stems from another question, which is how to pause a program before it terminates. Using cin.get() works only when there are no characters left in the stream. The instant you throw a cin>> foo; in the code, suddenly the solution fails. You need to clear out any leftover characters from the stream before it'll work again.

So how do you fix the problem? The good news is that unless you want to get picky, it's as simple as a loop: C++ Syntax (Toggle Plain Text)

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

This loop simply reads characters until end-of-file or a newline is read. It's generally assumed that interactive input in C++ is line-oriented and you're guaranteed to have a clean buffer after reading a newline. While that's not true (input doesn't have to be line-oriented), it's wide spread enough that we can assume it for the purposes of this thread.

So what's wrong with this approach? Nothing. In fact, this is about as good as it gets unless you want to dig down and fix the subtle problems. But before we look at the problems, here's an alternative solution that does the same thing in a different way:

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

The ignore member function of std::istream will read and discard up to N characters or until a delimiter. In the above example, N is represented by the largest value of the streamsize data type, and the delimiter is a newline. It works equally well with just a large value (80 is common): C++ Syntax (Toggle Plain Text) in.ignore ( 80, '\n' ); However, the streamsize data type is more likely to be an accurate representation of the size of the buffer that the stream is using, and it's more likely to work all of the time. This is the solution that I recommend.

So what's wrong with this? There are two notable problems. The first is easy to fix, and it stems from the fact that istream isn't very flexible. istream is actually a typedef for basic_istream. If you want a wide stream to work with ignore_line, you're SOL with istream. So the trick is to use basic_istream<> instead:

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

Now ignore_line is a template function that will derive the type of characters that the stream contains from the first argument. You can pass any stream that derives from or specializes basic_istream, and the problem is gone. Instead of just '\n', it's a good idea to use widen on the literal so that it will be properly converted to a wider type if necessary. Nice and easy.

The second problem is harder. Much harder. It's harder because standard iostreams seem to block your way at every turn with lack of portability or undesired features. In fact, it's impossible to completely fix the problem. The problem is that the behavior is different depending on the contents of the stream. For example:

    #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"; 
} 

Run this program three times:

Input: "asdf" Output: The program finishes without any more input from you.

Input: Just hit Enter Output: The program waits for you to hit Enter one more time.

Input: Signal EOF Output: The program waits for you to hit Enter one more time.

The problem is that the stream is empty. If you hit Enter immediately, a newline is placed on the stream and consumed by cin.get. Likewise with signaling EOF. At that point there's nothing left in the stream and cin.ignore stops everything until you type more characters. This is because cin.ignore is a blocking read. If there's nothing to read, it waits.

What we'd like it to do is not block for any of those three cases. The good news is that the iostream library supports some possible solutions. The bad news is that these are dead ends. Here are two common ones:

The sync member function The istream class supports a member function called sync. Why it has such a function is under debate, because nobody can agree on what it should be doing. Even Bjarne Stroustrup himself incorrectly stated that it discards all characters in the stream:

  #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"; 
} 

When this works, it works beautifully. The bad news is that the C++ standard doesn't require sync to do anything like discarding extraneous characters. This solution is non-portable.

The in_avail member function The next step is to look at the in_avail member function of istream's stream buffer. At first glance it looks like this member function will tell you how many characters are in the stream, and if it returns 0, you can refrain from calling 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";
 }

As with sync, when this works, it works great. But once again, the standard raises a wall by saying that in_avail isn't required to give you an accurate representation of the characters in the stream. In fact, some popular implementations have a strictly conforming in_avail that always returns 0. Not very useful. Now we have to get creative.

The putback member function

 #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";
 } 

This looks very promising because at first glance, it seems that you can attempt to push back a newline. If the operation fails, the last read character wasn't a newline and you're free to call ignore without blocking. If the operation succeeds, the newline is back and you can remove it with a single character ignore.

Sadly, it doesn't work. putback isn't not required to do any of this predictably, which raises the question of why it's even available.

But putback actually takes us close to a solution that seems plausible enough to work most of the time. Instead of relying on putback to fail or not, we can guarantee that the last read character is put back by using the sungetc member function of the stream's buffer. The trick is to unget the last character, then read it again and test it against a newline:

  #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";
 }

The reason we use sungetc instead of istream's unget is because unget returns the stream, but sungetc returns either the character that was pushed back, or EOF. This way we can tell if the function failed or not more easily.

If sungetc fails, one of the following will be true:

1) The stream is in an error state. 2) There are no characters to unget. 3) The stream doesn't support ungetting characters.

If sungetc succeeds, there will always be a character to read and test against the newline. If that character matches a newline, then the last read character was also a newline and we don't need to call ignore. If the character doesn't match, then a full line hasn't been read and we can safely call ignore without blocking.

If the stream is in an error state, that's something the calling code has to deal with. If there are no characters to unget, then that's precisely what this solution is designed to properly handle. But, if the stream doesn't support ungetting characters, that's an issue. The ignore_line function will always fail to discard characters, so for those implementations that don't support ungetting characters, we can add a flag that forces an ignore. It's sometimes useful to know how many characters were ignored as well, so let's add that too and we have the final solution:

   #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; 
}

Just for good measure, I'll also include a manipulator that calls ignore_line and also a manipulator that uses ignore_line to pause the program. That way the unwashed masses can stop using system ( "PAUSE" ); and 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();  
 } 
}; 

Now all three cases behave identically:

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

And the moral of the story is: It's never as simple as it seems, writing portable code that does what you want is extremely difficult, and the iostream library is a huge mess.

NOTE: If you are a beginner forget everything and just understand there is a flushing problem and use cin

Gradygrae answered 3/2, 2012 at 6:29 Comment(2)
NOTE: If u r beginner forget everything about cin.getline and just understand there is a flushing problem and use cinSelma
Thank you for this answer! It is ridiculous that an everyday problem like this has such a complicated solution.Kendre
A
1

Taking a look at the getline prototype, you'll see that you need two things:

  1. An istream object
  2. A string to for the result
  3. (optionally) the "new line" delimiter (default is '\n'.

In your case, it seems that the delimiter might be the default: '\n'; if not, you can specify what character separates each value (maybe it's a space ' ' or something). The cin stream would be you istream object, and each of the properties of employees[count] will serve as the second parameter. In summary, getline(cin, employees[count].name); should do the trick.

Andromada answered 3/2, 2012 at 6:37 Comment(0)
S
1

You can use getline function that takes input stream as first argument. It's prototype looks like this: basic_istream<...>& getline(basic_istream<...>& _Istr, basic_string<...>& _Str)

But then you have to think about that you are dealing with std::string and parse it as a type that you actually need (char*, int, whatever):

#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;
    //...
}
Signalment answered 3/2, 2012 at 7:1 Comment(0)
L
0
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;
    }
Laurentian answered 3/2, 2012 at 6:48 Comment(0)
S
0

istream& getline (char* s, streamsize n ); istream& getline (char* s, streamsize n, char delim ); Get line from stream

Extracts characters from the input sequence and stores them as a c-string into the array beginning at s.

Characters are extracted until either (n - 1) characters have been extracted or the delimiting character is found (which is delim if this parameter is specified, or '\n' otherwise). The extraction also stops if the end of file is reached in the input sequence or if an error occurs during the input operation.

If the delimiter is found, it is extracted and discarded, i.e. it is not stored and the next input operation will begin after it. If you don't want this character to be extracted, you can use member get instead.

The ending null character that signals the end of a c-string is automatically appended to s after the data extracted.

The number of characters read by this function can be obtained by calling to the member function gcount.

A global function with the same name exists in header . This global function provides a similar behavior, but with standard C++ string objects instead of c-strings: see getline (string).

Parameters s A pointer to an array of characters where the string is stored as a c-string. n Maximum number of characters to store (including the terminating null character). This is an integer value of type streamsize. If the function stops reading because this size is reached, the failbit internal flag is set. delim The delimiting character. The operation of extracting successive characters is stopped when this character is read. This parameter is optional, if not specified the function considers '\n' (a newline character) to be the delimiting character.

Return Value The function returns *this.

MSDN EXAMPLE:

// 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;
}

And for your situation with structure object, u have to use the objectname.datamember to access objects data:

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);
    }
Selma answered 3/2, 2012 at 7:0 Comment(3)
When I do that for the name and title, the program starts, but it displays "Name" and "Title" at the same time and only records the title. WHat wrong?Wearable
its the Flushing problem. If u really wanna know read through the answer!!Selma
better use cin so that u can leave out thinking of getting the input in the required formatsSelma

© 2022 - 2024 — McMap. All rights reserved.