1

I have a question about the difference between these two pieces of code:

char buffer5[5];
cin.get(buffer5, 5);
cout << buffer5;

cin.get(buffer5, 5);
cout << buffer5;

and

char buffer4;
while (cin.get(buffer4))
{
    cout << buffer4;
}

In the first piece of code, the code gets 5 characters and puts it in buffer5. However, because you press enter, a newline character isn't put into the stream when calling get(), so the program will terminate and will not ask you for another round of 5 characters.

In the second piece of code, cin.get() waits for input to the input stream, so the loop doesn't just terminate (I think). Lets say I input "Apple" into the input stream. This will put 5 characters into the input stream, and the loop will print all characters to the output. However, unlike the first piece of code, it does not stop, even after two inputs as I can continuously keep inputting.

Why is it that I can continuously input character sequences into the terminal in the second piece of code and not the first?

ZERO
  • 316
  • 7
  • 16
  • 5
    `get` returns an `istream &`, which you're never causing to evaluate to false for the loop condition. Try entering your system's EOF character. – chris Aug 28 '12 at 18:19
  • @chris:- there is one more box below my comment known as `Your Answer`.... – perilbrain Aug 28 '12 at 18:26
  • @Anonymous, I personally think a bit more work could go into an answer than that. – chris Aug 28 '12 at 18:32
  • @chris, if it wasn't for simple answers I'd only have half the points I have today! Seriously, if it's a genuine answer to the question you should post it as such. Only if it's speculation would a comment be preferred. – Mark Ransom Aug 28 '12 at 19:33

1 Answers1

5

First off, "pressing enter" has no special meaning to the IOStreams beyond entering a newline character (\n) into the input sequence (note, when using text streams the platform specific end of line sequences are transformed into a single newline character). When entering data on a console, the data is normally line buffered by the console and only forwarded to the program when pressing enter (typically this can be turned off but the details of this are platform specific and irrelevant to this question anyway).

With this out of the way lets turn our attention to the behavior of s.get(buffer, n) for an std::istream s and a pointer to an array of at least n characters buffer. The description of what this does is quite trivial: it calls s.get(buffer, n, s.widen('\n')). Since we are talking about std::istream and you probably haven't changed the std::locale we can assume that s.widen('\n') just returns '\n', i.e., the call is equivalent to s.get(buffer, n, '\n') where '\n' is called a delimiter and the question becomes what this function does.

Well, this function extracts up to m = 0 < n? n - 1: 0 characters, stopping when either m is reached or when the next character is identical to the delimiter which is left in the stream (you'd used std::istream::getline() if you'd wanted the delimiter to be extracted). Any extracted character is stored in the corresponding location of buffer and if 0 < n a null character is stored into location buffer[n - 1]. In case, if no character is extracted std::ios_base::failbit is set.

OK, with this we should have all ingredients to the riddle in place: When you entered at least one character but less than 5 characters the first call to get() succeeded and left the newline character as next character in the buffer. The next attempt to get() more characters immediately found the delimiter, stored no character, and indicated failure by setting std::ios_base::failbit. It is easy to verify this theory:

#include <iostream>

int main()
{
    char buffer[5];
    for (int count(0); std::cin; ++count) {
        if (std::cin.get(buffer, 5)) {
            std::cout << "get[" << count << "]='" << buffer << "'\n";
        }
        else {
            std::cout << "get[" << count << "] failed\n";
        }
    }
}

If you enter no character, the first call to std::cin.get() fails. If you enter 1 to 4 characters, the first call succeeds but the second one fails. If you enter more than 4 characters, the second call also succeeds, etc. There are several ways to deal with the potentially stuck newline character:

  1. Just use std::istream::getline() which behaves the same as std::istream::get() but also extracts the delimiter if this is why it stopped reading. This may chop one line into multiple reads, however, which may or may not be desired.
  2. To avoid the limitation of a fixed line length, you could use std::getline() together with an std::string (i.e., std::getline(std::cin, string)).
  3. After a successful get() you could check if the next character is a newline using std::istream::peek() and std::istream::ignore() it when necessary.

Which of these approaches meets your needs depends on what you are trying to achieve.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • In your code, the `else` condition executes when there is no longer anything in the input stream. However, in the while loop in my question, the program continuously runs, assuming there is actually stuff in the input stream. chris said in the comments that get() always returns istream&, but contradicts your answer. Also a better way to say my question is why doesn't the while loop ever terminate, even when you enter in various strings (newline character is also put into the stream when the `Enter` key is pressed)? – ZERO Aug 28 '12 at 20:07
  • Actually, Chris's answer is entirely in line with mine: both versions of `get()` return an `std::istream` which indicates failure and converts to a Boolean value you can test: `true` for no error flags set and `false` for one of the error flags `std::ios_base::failbit` or `std::ios_base::bad_bit` being set. Your `while` loop just extracts individual characters and is successful a long as there are characters coming. The overload `std::istream::get(char&)` only fails (i.e., sets `std::ios_base::failbit`) if there are no more characters to be extracted. – Dietmar Kühl Aug 28 '12 at 20:21
  • Hmm, is there any character/input that may break the loop? I thought that a newline character or even a null character would break the loop. – ZERO Aug 28 '12 at 20:23
  • 1
    To have your `while` loop fail you need to terminate you stream. You can do this, e.g., by redirecting a file into your program (typically using something like `program < file` on the command line) or by typing the "end of file" character (on UNIXes Ctrl-D, on Windows Ctrl-Z, but possibly also something else depending on the system; this isn't standardized by the C++ standard) at an opportune place (often it has to be immediately after a newline). – Dietmar Kühl Aug 28 '12 at 20:23
  • @DietmarKühl " If you enter more than 4 characters, the second call also succeeds" it fascinates me as we should enter less than 5 char but we are entering more than it and it working fine so I checked it some another example int a; char s[20]; cin>>a; cin>>s; input : 23bonapart now cout< – Abhishek Mane Apr 10 '21 at 20:11
  • @DietmarKühl char ch,s[20]; std::cin.get(s,20); std::cin>>ch; std::cout<<"x"< – Abhishek Mane Apr 10 '21 at 21:48
  • 1
    @AbhishekMane: Note that formatted input, e.g., using `std::cin >> ch`, skips leading whitespace (unless you disabled doing so, e.g., using `std::cin >> std::noskipws`). Correspondingly, `ch` won’t contain a newline (or any other whitespace). Also, **never** use formatted input without setting the stream’s size: `std::cin >> std::setw(20) >> s;` as otherwise you built skmething which can easily overflow a buffer. I think the answer to your main confusions is: the formatted input operators generally terminate when they encounter a character which doesn’t fit the format. – Dietmar Kühl Apr 10 '21 at 22:04
  • @DietmarKühl thanks first of all, I got lot of clarity in concepts . __ch won’t contain a newline (or any other whitespace)__ right but there is nothing in input stream except `\n` and `std::cin>>ch` skips `\n` (leading whitespace char ) so why `std::cin>>ch` ask input from keyboard but on contrary `std::cout< – Abhishek Mane Apr 11 '21 at 09:16
  • @DietmarKühl please do reply I have only one above query – Abhishek Mane Apr 13 '21 at 10:53
  • 1
    @AbhishekMane: Most likely the input operation failed and `ch` just contained whatever it contained before [or is actually uninitialized but happens to contain something which gets formatted as a space]: did you verify that the read of `ch` was successful? You always need to verify that a read was successful before using the result of reading. – Dietmar Kühl Apr 14 '21 at 09:46
  • @DietmarKühl I got it. Thanks – Abhishek Mane Apr 15 '21 at 03:48
  • @DietmarKühl https://stackoverflow.com/q/67772282/11862989 can you answer this question please . – Abhishek Mane Jun 02 '21 at 12:23
  • @AbhishekMane: This question has many answers, it has nothing to do with things I have a huge interest in, and stackoverflow is a volunteering site: I’ll choose the questions I want to provide an answer for. – Dietmar Kühl Jun 02 '21 at 16:18
  • @DietmarKühl okay and sorry I troubled you and I was not asking you but I am requesting you and that answer I got just now. let it be. Again sorry for troubling you. – Abhishek Mane Jun 02 '21 at 16:22