1

With the code below, I want the user to write a text in the terminal, and then print out the last sentence of the text. Maybe I should mention that I'm running on a Linux desktop.

#include <string>
#include <iostream> 

int main()
{
    std::string user_text{};

    while(std::getline(std::cin, user_text))
    {

    }

    std::cout << "Text: " << user_text << std::endl;

    return 0;
}

Anyways if I, after running the program, write for example:

Hi my name is

And then press 'ctrl+d', the output will indeed be "Text: Hi my name is"

However if I instead do this:

Hi my name is 'press enter'

Name my is hi 'press enter'

And then press 'ctrl+d'. The output will be "Text: ". Why is this? Shouldn't getline stop when I have pressed 'ctrl+d'?

Thanks in advance!

Stack Danny
  • 7,754
  • 2
  • 26
  • 55
exer240
  • 27
  • 4
  • 3
    The first time you terminate the input after writing a complete line. The second time you terminate input right after pressing enter, i.e. after reading an _empty line_. – Lukas-T May 20 '21 at 19:30
  • Clearer: The second example has _three_ lines, two of which have text – Mooing Duck May 20 '21 at 20:26

3 Answers3

4

std::getline() erases the output std::string before attempting to read from the stream.

In your second case, the first 2 calls to std::getline() have already read everything you have typed in, there is nothing left when you press CTRL-D during the 3rd call, so there is nothing for std::getline() to output into the std::string.

Save the last successful line read to a separate variable, eg:

std::string user_text, line;

while(std::getline(std::cin, line))
{
    user_text = line;
}

std::cout << "Text: " << user_text << std::endl;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Not sure about this answer, the documentation on [cppreference.com](https://en.cppreference.com/w/cpp/string/basic_string/getline) is not completely clear, but [when I try it out, it does not seem to clear the string when the read fails](https://godbolt.org/z/5z4csYd7T). – asynts May 20 '21 at 19:47
  • 1
    My understanding is that `std::getline` gets everything until the next `\n` or end-of-file (That's not a character!). Thus it returns the empty line he already started. – asynts May 20 '21 at 19:51
  • @asynts "*After constructing and checking the sentry object, performs the following: 1) Calls `str.erase()`*" - seems pretty clear to me. – Remy Lebeau May 20 '21 at 19:51
  • That's why I am confused, because it doesn't do that. In [the example](https://godbolt.org/z/5z4csYd7T), after the second call (which fails) `value` is still `foo`. – asynts May 20 '21 at 19:52
  • 1
    @asynts your example does not take into account that the 2nd `value` might be evaluated before the 2nd `getline()` is executed. Put a `;` between `getline()` and `<< value` and see what happens – Remy Lebeau May 20 '21 at 19:53
  • [Nope, that's not it.](https://godbolt.org/z/TMe6751hW) – asynts May 20 '21 at 19:54
  • You are right about the evaluation order though, it's undefined behavior to access `value` when it is modified by `std::getline` without a sequence point. [This example should be without undefined behaviour.](https://godbolt.org/z/EGjvMTT6h) – asynts May 20 '21 at 19:57
  • @asynts then godbolt's implementation of `getline()` is not conforming to documented behavior. – Remy Lebeau May 20 '21 at 19:59
  • 2
    Actually, the standard (or rather this unofficial draft) says that [`str.erase()` is called if `sentry` converts to `true`](http://eel.is/c++draft/string.io#6) which happens if `std::cin.good()` returns `true` which may, or may not be the case. My understanding is, that depending on the implementation, the end-of-file bit could be set, thus `str.erase()` could be called. – asynts May 20 '21 at 20:14
1

std::getline is working as intended: it's getting a line. If you press enter, it creates a new, empty line; if you then press ctrl+d, you're terminating std::getline, which returns that (empty) line's contents.

From the docs:

getline reads characters from an input stream and places them into a string:

  1. Behaves as UnformattedInputFunction, except that input.gcount() is not affected. After constructing and checking the sentry object, performs the following:
    1. Calls str.erase()
    2. Extracts characters from input and appends them to str until one of the following occurs (checked in the order listed) a) end-of-file condition on input, in which case, getline sets eofbit. b) the next available input character is delim, as tested by Traits::eq(c, delim), in which case the delimiter character is extracted from input, but is not appended to str. c) str.max_size() characters have been stored, in which case getline sets failbit and returns.
    3. If no characters were extracted for whatever reason (not even the discarded delimiter), getline sets failbit and returns.
  2. Same as getline(input, str, input.widen('\n')), that is, the default delimiter is the endline character.
Nick Reed
  • 4,989
  • 4
  • 17
  • 37
0

Ctrl+D causes the process's read from the terminal to return immediately. If you press Ctrl+D after typing: Hi my name is, the process will read: Hi my name is. getline will not find a \n and will restart reading. Then you press Ctrl+D a second time (you didn't say it but I am sure you did). And this will interrupt the read, causing it to return 0, which is as-if the terminal was closed. getline will then return the current value: Hi my name is.

In the second case, you haven't typed anything since the last \n, so when you press Ctrl+D, read directly returns 0 and getline returns with an empty string.

fjardon
  • 7,921
  • 22
  • 31
  • 1
    I didn't say the process was terminated. I say the process's `read` returned immediately. – fjardon May 20 '21 at 19:36
  • https://unix.stackexchange.com/questions/110240/why-does-ctrl-d-eof-exit-the-shell – fjardon May 20 '21 at 19:38
  • It will read `until` it reaches EOF or \n. In this case the first call to read will stop after `Hi my name is` because Ctrl+D will interrupt it. As there is no EOF or '\n' getline will keep reading. – fjardon May 20 '21 at 19:41
  • What would you like to see ? – fjardon May 20 '21 at 19:43