Getline Keeps on Getting Newline Character. How to Avoid This

Getline keeps on getting newline character. How can I avoid this?

Your cin >>N stops at the first non-numeric character, which is the newline. This you have a getline to read past it, that's good.

Each additional getline after that reads the entire line, including the newline at the end. By putting in a second getline you're skipping half your input.

stop getline from discarding newline character

getline( the_stream, the_string );
the_string += '\n';

C Ignore newline character \n using getline

Is adding one of these lines (OPTION 1, OPTION 2) the correct way of doing this?

OPTION 1 is undefined behavior according to the standard so I wouldn't recommend it even if it does work on your system.

OPTION 2 is better and it will "eat" the '\n' left by the scanf which is what you want. But it will also "eat" any later input just consisting of a newline.

If you just want to "eat" the newline from the input of number I'll probably do:

int main() {
int number;
char *line = NULL;
size_t len = 0;
getline(&line, &len, stdin);
if (sscanf(line, "%d", &number) != 1)
{
// Illegal input
exit(1);
}
for (int i = 0; i < number; ++i) {
// ------------------- line = NULL; Delete this - see below
getline(&line, &len, stdin);
printf("%s", line);
}
free(line);
retur 0;
}

Notice that you shall only set line to NULL in the start of the program. Setting it to NULL in each loop causes memory leaks. Also notice the free(line)

Why do cin and getline exhibit different reading behavior?

reading behavior of cin and getline.

cin does not "read" anything. cin is an input stream. cin is getting read from. getline reads from an input stream. The formatted extraction operator, >>, reads from an input stream. What's doing the reading is >> and std::getline. std::cin does no reading of its own. It's what's being read from.

first cin read up until the "\n". once it hit that "\n", it increments the
cursor to the next position

No it doesn't. The first >> operator reads up until the \n, but does not read it. \n remains unread.

The second >> operator starts reading with the newline character. The >> operator skips all whitespace in the input stream before it extracts the expected value.

The detail that you're missing is that >> skips whitespace (if there is any) before it extracts the value from the input stream, and not after.

Now, it is certainly possible that >> finds no whitespace in the input stream before extracting the formatted value. If >> is tasked with extracting an int, and the input stream has just been opened and it's at the beginning of the file, and the first character in the file is a 1, well, the >> just doesn't skip any whitespace at all.

Finally, std::getline does not skip any whitespace, it just reads from the input stream until it reads a \n (or reaching the end of the input stream).

Why does std::getline() skip input after a formatted extraction?

Why does this happen?

This has little to do with the input you provided yourself but rather with the default behavior std::getline() has. When you provided your input for the age (std::cin >> age), you not only submitted the following characters, but also an implicit newline was appended to the stream when you typed Enter:

"10\n"

A newline is always appended to your input when you select Enter or Return when submitting from a terminal. It is also used in files for moving toward the next line. The newline is left in the buffer after the extraction into age until the next I/O operation where it is either discarded or read. When the flow of control reaches std::getline(), it will see "\nMr. Whiskers" and the newline at the beginning will be discarded, but the input operation will stop immediately. The reason this happens is because the job of std::getline() is to attempt to read characters and stop when it finds a newline. So the rest of your input is left in the buffer unread.

Solution

cin.ignore()

To fix this, one option is to skip over the newline before doing std::getline(). You can do this by calling std::cin.ignore() after the first input operation. It will discard the next character (the newline character) so that it is no longer in the way.

std::cin >> age;
std::cin.ignore();
std::getline(std::cin, name);

assert(std::cin);
// Success!

std::ws

Another way to discard the whitespace is to use the std::ws function which is a manipulator designed to extract and discard leading whitespace from the beginning of an input stream:

std::cin >> age;
std::getline(std::cin >> std::ws, name);

assert(std::cin);
// Success!

The std::cin >> std::ws expression is executed before the std::getline() call (and after the std::cin >> age call) so that the newline character is removed.

The difference is that ignore() discards only 1 character (or N characters when given a parameter), and std::ws continues to ignore whitespace until it finds a non-whitespace character. So if you don't know how much whitespace will precede the next token you should consider using this.

Match the operations

When you run into an issue like this it's usually because you're combining formatted input operations with unformatted input operations. A formatted input operation is when you take input and format it for a certain type. That's what operator>>() is for. Unformatted input operations are anything other than that, like std::getline(), std::cin.read(), std::cin.get(), etc. Those functions don't care about the format of the input and only process raw text.

If you stick to using a single type of formatting then you can avoid this annoying issue:

// Unformatted I/O
std::string age, name;
std::getline(std::cin, age);
std::getline(std::cin, name);

or

// Formatted I/O
int age;
std::string firstName, lastName;
std::cin >> age >> firstName >> lastName;

If you choose to read everything as strings using the unformatted operations you can convert them into the appropriate types afterwards.

How do I stop program from skipping over getline?

string command;
cin>>command;

after this just eat the end of the line

string restOfLine;
getline(cin, restOfLine);

Otherwise the '\n' in the line where you input command is not consumed and the next readline reads just it. HTH

getline keeps waiting on standart input

if there is no input in the standard input it just sits there waiting and never actually exits the loop, so the program basically pauses until something is entered, and then just continues the loop once more. Can someone tell me why getline doesn't just cause an exit condition when there is nothing on stdin

It just behaves as expected. What is "nothing on stdin" actually? Did you mean an empty input? In that case you might want to change your loop condition to

while(getline(cin, line) && !line.empty()){

Also as mentioned in the comments CTRL-Z or CTRL-D (depends on OS) followed with ENTER input may end the loop.

std::getline() reads carriage return \r into the string, how to avoid that?

The problem is that in Windows a newline is represented as CR + LF which is: "\r\n" and in Unix it is LF which is just "\n".

Your std::getline(...) command is reading till the "\n" in "leer\r\n" and discards the "\n", your resulting string will be:

"leer\r"

To solve this problem and convert files between Unix/Windows there are the 2 tools dos2unix and unix2dos. The Ubuntu equivalents are fromdos and todos, you will need fromdos to convert your Windows text file to a Unix text file.

To test wether a file uses CR + LF or LF you can do:

dos2unix < myfile.txt | cmp -s - myfile.txt

which was ansered here on the Unix & Linux StackExchange site.



And it seems like it saves "leer\\r" in the object instead of only "leer" but I tried to replace "leer\\r" as well and it still doesn`t work. I still cant understand why my if (obj.m_Sternbild == "leer\\r") didn`t work because imo it should have worked?

It should be:

if (obj.m_Sternbild == "leer\r")

without escaping the backslash \, because \r is read into the string.

Edit:

As @FreelanceConsultant in the comment below write: The above answer is not a general solution. Because a binary compiled either on Windows or Unix should work for text files for both platforms.

There are two solutions for that.

The obvious one is, to compare against two different versions of the input. With std::getline the Windows result is "leer\r" and Unix result is "leer".

if (obj.m_Sternbild == "leer\r" || obj.m_Sternbild == "leer")

Another solution would be to normalize the newline representation to one form and only check against that. It is a matter of taste and performance, because you would need to create new strings. See his answer as example.



Related Topics



Leave a reply



Submit