Checking ignore() for values - c++

When you use ignore() in C++, is there a way to check those values that were ignored? I basically am reading some # of chars and want to know if I ignored normal characters in the text, or if I got the newline character first. Thanks.

I don't believe so - you'd have to "roll your own".
In other words, I think you'd have to write some code that read from the stream using get(), and then add some logic for keeping what you need and ignoring the rest (whilst checking to see what you're ignoring).

If you don't actually want to ignore the characters, don't use ignore() to extract them. get() can do the same job but also stores the extracted characters so that you can inspect them later.

If you provide the optional delim parameter to ignore(), then it can stop at a newline:
streampos old = is.tellg();
is.ignore(num, '\n');
if (is.tellg() != old + num) {
// didn't ignore "num" characters, if not eof or error then we
// must have reached a newline character.
}
There's a snag, though - when ignore() hits the delimiter, it ignores that too. So if you hit the delimiter exactly at the end of your set of ignored characters, then tellg() will return old + num. AFAIK there's no way to tell whether or not the last character ignored was the delimiter. There's also no way to specify a delimiter that isn't a single character.
I also don't know whether and when this is likely to be any faster than just reading num bytes and searching it for newlines. My initial thought was, "which part of the difference between ignore() and read() is non-obvious?" ;-)

Related

Why does ignore() before getline() take one less character input?

I am using the getline() function in C++, but there is a problem that the input starts from the second character. I used the ignore() function to erase what remains in the buffer first, emptying the buffer and receiving input. How can I empty the buffer and receive input properly?
Above is the execution result. I previously used the ignore() function and the getline() function to empty the buffer and receive input because there may be some leftovers in the buffer before.
In other programs that write like that, it also receives integer input before.
void InputValue(string *st) {
//cin.clear();
cin.ignore();
getline(cin, *st);
}
int main(void) {
string str;
InputValue(&str);
cout << str;
return 0;
}
In the beginning, immediately after input, stdin (your input buffer) contains :
abcd
Those character are waiting to be extracted in whatever manner you choose. Since you are reading from stdin (using std::cin) using getline() from the stream into a std::string, getline() will consume all characters in the line, reading and discarding the trailing '\n' with all characters stored in your std::string. There are no characters left in stdin that need to be extracted using std::cin.ignore()
When you call std::cin.ignore() before getline() you are reading (and discarding) the first character waiting to be read in stdin. With the example characters above, after calling std::cin.ignore(), and extracting and discarding the character, the following is left in stdin:
bcd
So now when you do call getline (std::cin, *st); "bcd" are the only characters that are available. That is the reason why you miss the first character.
While you can std::cin.ignore() one character, the .ignore() member function is usually used to read/discard all remaining characters in a line using the form
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Where #include <limits> provides the constants representing the maximum number of the type specified, above <std::streamsize>::max() (e.g. the maximum value that size_t can represent). A large constant is used to ensure the remainder of the line, no matter how long, is consumed and discarded leaving std::cin either empty or with the first character of the next line (in a multi-line buffer) waiting to be read.
std::basic_istream::ignore can also read up to a delimiter character reading/discarding less than the whole line. That can be useful when reading separated fields, or in cases where you use a delimiter with getline() to read less than an entire line.
You either perform your read operation first and then call ignore() to discard unwanted characters to the end of the line, or you can call ignore() first with either a specified number of characters (default 1) or a delimiter to discard the beginning portion of a line up to the first character you want to began your read with.
As you have used it above, it will simply chop off the first character of whatever the user enters -- which does not seem to be what you want. In fact, ignore() isn't even needed above. Remove it and see if the missing character problem doesn't go away.

Does istream::ignore discard more than n characters?

(this is possibly a duplicate of Why does std::basic_istream::ignore() extract more characters than specified?, however my specific case doesn't deal with the delim)
From cppreference, the description of istream::ignore is the following:
Extracts and discards characters from the input stream until and including delim.
ignore behaves as an UnformattedInputFunction. After constructing and checking the sentry object, it extracts characters from the stream and discards them until any one of the following conditions occurs:
count characters were extracted. This test is disabled in the special case when count equals std::numeric_limitsstd::streamsize::max()
end of file conditions occurs in the input sequence, in which case the function calls setstate(eofbit)
the next available character c in the input sequence is delim, as determined by Traits::eq_int_type(Traits::to_int_type(c), delim). The delimiter character is extracted and discarded. This test is disabled if delim is Traits::eof()
However, let's say I've got the following program:
#include <iostream>
int main(void) {
int x;
char p;
if (std::cin >> x) {
std::cout << x;
} else {
std::cin.clear();
std::cin.ignore(2);
std::cout << "________________";
std::cin >> p;
std::cout << p;
}
Now, let's say I input something like p when my program starts. I expect cin to 'fail', then clear to be called and ignore to discard 2 characters from the buffer. So 'p' and '\n' that are left in the buffer should be discarded. However, the program still expects input after ignore gets called, so in reality it's only get to the final std::cin>>p after I've given it more than 2 characters to discard.
My issue:
Inputting something like 'b' and hitting Enter immediately after the first input (so 2 after the characters get discarded, 'p' and '\n') keeps 'b' in the buffer and immediately passes it to cin, without first printing the message. How can I make it so that the message gets printed immediately after the two characters are discarded and then << is called?
After a lot of back and forth in the comments (and reproducing the problem myself), it's clear the problem is that:
You enter p<Enter>, which isn't parsable
You try to discard exactly two characters with ignore
You output the underscores
You prompt for the next input
but in fact things seem to stop at step 2 until you give it more input, and the underscores only appear later. Well, bad news, you're right, the code is blocking at step 2 in ignore. ignore is blocking waiting for a third character to be entered (really, checking if it's EOF after those two characters), and by the spec, this is apparently the correct thing to do, I think?
The problem here is the same basic issue as the problem you linked just a different manifestation. When ignore terminates because it's read the number of characters requested, it always attempts to reads one more character, because it needs to know if condition 2 might also be true (it happened to read the last character so it can take the appropriate action, putting cin in EOF state, or leaving the next character in the buffer for the next read otherwise):
Effects: Behaves as an unformatted input function (as described above). After constructing a sentry object, extracts characters and discards them. Characters are extracted until any of the following occurs:
n != numeric_limits::max() (18.3.2) and n characters have been extracted so far
end-of-file occurs on the input sequence (in which case the function calls setstate(eofbit), which may throw ios_base::failure (27.5.5.4));
traits::eq_int_type(traits::to_int_type(c), delim) for the next available input character c (in which case c is extracted).
Since you didn't provide an end character for ignore, it's looking for EOF, and if it doesn't find it after two characters, it must read one more to see if it shows up after the ignored characters (if it does, it'll leave cin in EOF state, if not, the character it peeked at will be the next one you read).
Simplest solution here is to not try to specifically discard exactly two characters. You want to get rid of everything through the newline, so do that with:
std::cin.ignore(std::numeric_limits<std::stringsize>::max(), '\n');
instead of std::cin.ignore(2);; that will read any and all characters until the newline (or EOF), consume the newline, and it won't ever overread (in the sense that it continues forever until the delimiter or EOF is found, there is no condition under which it finishes reading a count of characters and needs to peek further).
If for some reason you want to specifically ignore exactly two characters (how do you know they entered p<Enter> and not pabc<Enter>?), just call .get() on it a couple times or .read(&two_byte_buffer, 2) or the like, so you read the raw characters without the possibility of trying to peek beyond them.
For the record, this seems a little from the cppreference spec (which may be wrong); condition 2 in the spec doesn't specify it needs to verify if it is at EOF after reading count characters, and cppreference claims condition 3 (which would need to peek) is explicitly not checked if the "delimiter" is the default Traits::eof(). But the spec quote found in your other answer doesn't include that line about condition 3 not applying for Traits::eof(), and condition 2 might allow for checking if you're at EOF, which would end up with the observed behavior.
Your problem is related to your terminal. When you press ENTER, you are most likely getting two characters -- '\r' and '\n'. Consequently, there is still one character left in the input stream to read from. Change that line to:
std::cin.ignore(10, '\n'); // 10 is not magical. You may use any number > 2
to see the behavior you are expecting.
Passing exact number of characters in buffer will do the trick:
std::cin.ignore(std::cin.rdbuf()->in_avail());

Why does istream::get set cin.fail when '\n' is the first character?

Why these two functions istream::get(char*, streamsize) and istream::get(char*, streamsize, char) set the cin.fail bit when they find '\n' as the first character in the cin buffer?
As can be seen here, that's the behavior of the two overloads mentioned above. I'd like to know what was the purpose in designing these functions this way ? Note that both functions leave the character '\n' in the buffer, but if you call any of them a second time, they will fail because of the newline character, as shown in the link. Wouldn't it make more sense to make these two functions not to leave the character '\n' in the buffer, as the overloads of the function istream::get() and istream::getline() do ?
With std::istream::getline, if the delimiting character is found it is extracted and discarded. With std::istream::get the delimiting character remains in the stream.
With getline you don't know, if the delimiting character was read and discarded or if just n - 1 characters where read. If you only want to read whole lines, you can use get and then peek for the next character and see if it is a newline or the given delimiter.
But if you want to read whole lines up to some delimiter, you might also use std::getline, which reads the complete line in any case.

using cin.get() function

char arr[100];
cin.get(arr,100);
Is this safe? Will the null-character be appended at the end even if I type more than 100 chars? or should I use cin.get(arr,99)?
When I type ENTER, is the end-of-line character part of the array or not?
The answers to both of your questions can be found here, but to reiterate:
The get method reads at most n - 1 characters. This means that the method expects the size of the buffer and not the number of characters to read. This method automatically appends a null character to the end.
The newline character is not extracted or stored in the array.
You may also want to consider using std::getline which you can use in conjunction with std::string.
1)is this safe. I mean will the null-character be appended
at the end. even if I typed more than 100 chars. or it must be cin.get(arr,99).
Taken from here.
The signature of get you are using looks like this:
basic_istream& get( char_type* s, std::streamsize count );
It will read at most count - 1 characters from the stream (in your case 99) or up until the delimiting character, which is by default \n. So if you type more than 100 characters, a call to get will read 99 of those characters and then append the null terminator \0 at the end.
2)also when I type ENTER, a newline get passed. so does this character is really a part of the array or not.
No, get read up until the delimiting character, so if you press ENTER, \n will be left in the stream as the next character to be read.
Advice:
Please use the site I linked to in order to understand how these functions work, and prefer std::string and std::getline if you are coding in C++.

istream get method behavior

I read istream::get and a doubt still hangs. Let's say my delimiter is actually the NULL '\0' character, what happens in this case? From what I read:
If the delimiting character is found, it is not extracted from the input sequence and remains as the next character to be extracted. Use getline if you want this character to be extracted (and discarded). The ending null character that signals the end of a c-string is automatically appended at the end of the content stored in s.
The reason I would prefer "get" over "readline" is because of the capability to extract the character stream into a "streambuf".
I dont' quite get your problem.
On the msdn website, for the get function, it says:
In all cases, the delimiter is neither extracted from the stream nor returned by the function. The getline function, in contrast, extracts but does not store the delimiter.
In all cases, the delimiter is neither extracted from the stream nor returned by the function. The getline function, in contrast, extracts but does not store the delimiter.
http://msdn.microsoft.com/en-us/library/aa277360(VS.60).aspx
I don't think your going to have a problem, since the msdn site tells that the delimiter is neither extracted from the stream, nor returned vy the function.
Or maybe I'm missing a point here?
If you have something like this, then delimiter will not get stuck in the input stream:
std::string read_str(std::istream & in)
{
const int size = 1024;
char pBuffer[size];
in.getline(pBuffer, size, '\0');
return std::string(pBuffer);
}
just an example if you have '\0' as delimiter and strings are not bigger than 1024 bytes.