C++ primer 5th 1.4.4 - c++

I'm a beginner of C++,while reading the book 《C++ Primer》 5th,I'm a little confused in chapter 1.4.4.
when I run the program in 1.4.4,here is the step in my computer:
#include <iostream>
int main()
{
// currVal is the number we're counting; we'll read new values into val
int currVal = 0, val = 0;
// read first number and ensure that we have data to process
if (std::cin >> currVal)
{
int cnt = 1; // store the count for the current value we're processing
while (std::cin >> val)
{ // read the remaining numbers
if (val == currVal) // if the values are the same
++cnt; // add 1 to cnt
else
{ // otherwise, print the count for the previous value
std::cout << currVal << " occurs " << cnt << " times" << std::endl;
currVal = val; // remember the new value
cnt = 1; // reset the counter
}
} // while loop ends here
// remember to print the count for the last value in the file
std::cout << currVal << " occurs " << cnt << " times" << std::endl;
} // outermost if statement ends here
return 0;
}
type the numbers:42 42 42 42 42 55 55 62 100 100 100
type Ctrl+D
the program run by itself(not waitting me to input Enter)
output the answer:
42 occurs 5 times
55 occurs 2 times
62 occurs 1 times
secondly type Ctrl+D
output the remained answer
100 occurs 3 times
my question is why I have to input second times of Ctrl+D,my code environment is Ubuntu+GCC,I also run it in VS2013,it only needs to input once Ctrl+D.
I've searched in stackoverflow,but I didn't got my answer.
Incorrect output. C++ primer 1.4.4
confused by control flow execution in C++ Primer example
C++ Primer fifth edtion book (if statement) is this not correct?

In Linux Ctrl+D does not unconditionally mean "end-of-file" (EOF). What it actually means is "push the currently pending input to whoever is waiting to read it". If the input buffer in non-empty, then hitting Ctrl+D does not create EOF condition at the end of the buffer. Only if you hit Ctrl+D when the input buffer is empty, only then it will produce EOF condition. (See here for a more technical explanation: https://stackoverflow.com/a/1516177/187690)
In your case you are inputting your data as a single line and then hitting Ctrl+D at the end. This pushes your input to your program and makes your program to read and process the data. But it does not produce EOF condition at the end of your input.
For this reason, once all input data is read by the cycle, your program does not see it as EOF. The cycle keeps waiting on empty input buffer for additional data. If at this point you press Ctrl+D again, it will be recognized as EOF and your program will exit the cycle and print the last line.
This is why you have to hit Ctrl+D twice: the first hit works pretty much as Enter key would. And only the second hit creates EOF condition.

It is possible that the program you have provided was not intended to accept input all at once as you have shown.
The reason it does not provide the output you expect is due to the fact that the program is still expecting input, because the >> operation's return value is still logically true/error-free. (It's blocked at: while (std::cin >> val)) This is so, because you have not provided an EOF to the input stream after the last 100. Said another way, your first Ctrl+D gets past the if (std::cin >> currVal). Your second Ctrl+D gets past the while (std::cin >> val).
See the accepted answer to this question for why the first Ctrl+D does not result in a eofbit error on your input stream: Why do I have to type ctrl-d twice? The bottom line is that Ctrl+D does not necessarily mean EOF; it results in a flush of the input stream.
Entering the numbers one at a time would provide the output you expect.
Alternatively, you could provide: 42 42 42 42 42 55 55 62 100 100 100\n.
http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/

Related

C++ istream input fails yet consumes data. Expected to be able read bad data after failure

My understanding of iostream has always been that when an input conversion fails, the data remains in the stream to be read after clearing the error. But I have an example that does not always work that way, and I would like to know if the behavior is correct. If so, could someone point me at some good documentation of the actual rules?
This small program represents the idea of using an i/o failure to read an optional repeat count and a string.
#include <iostream>
#include <string>
int main()
{
int cnt;
std::cout << "in: ";
std::cin >> cnt;
if(!std::cin) {
cnt = 1;
std::cin.clear();
}
std::string s;
std::cin >> s;
std::cout << "out: " << cnt << " [" << s << "]" << std::endl;
}
So, here's how it runs:
[me#localhost tmp]$ ./bother
in: 16 boxes
out: 16 [boxes]
[me#localhost tmp]$ ./bother
in: hatrack
out: 1 [hatrack]
[me#localhost tmp]$ ./bother
in: some things
out: 1 [some]
[me#localhost tmp]$ ./bother
in: 23miles
out: 23 [miles]
[me#localhost tmp]$ ./bother
in: #(#&$(##&$ computer
out: 1 [#(#&$(##&$]
So it mostly works. When there's a number first, it is read, then the string is. When I give a non-numeric first, the read fails, the count is set to 1 and the non-numeric input is read. But this breaks:
[me#localhost tmp]$ ./bother
in: + smith
out: 1 [smith]
The + fails the integer read because it's not enough to make a number, but the + does not remain on the stream to be picked up by the string read. Likewise with -. If it reads the + or - as a zero, that would be reasonable, but then the read should succeed and cnt should show that zero.
Perhaps this is correct behavior, and I've just always been wrong about what it is supposed to do. If so, what are the rules?
Your advice appreciated.
Plus and minus are valid parts of an integer and so are read, when the next character is read the stream fails and that character is left in the stream but the leading sign character is not put back into the stream.
See https://en.cppreference.com/w/cpp/locale/num_get/get for the full rules

while loop running for every digit/character from input

Hey guys beginner in C++ and coding in general. I am currently making a tictactoe program. For the part of the program I am validating user input. Since it is a 3x3 table, I want to make sure their input is an integer and that they choose a number between 1~9.
To do this I wrote
//Validating user input
void move() {
std::cout << "It's Player" << player << "'s turn!\n";
while(!(std::cin >> position)){
std::cout << "Please choose a NUMBER between 1~9!\n";
std::cin.clear();
std::cin.ignore();
}
while(position < 1 || position > 9){
std::cout << "Please choose a number BETWEEN 1~9!\n";
std::cin.clear();
std::cin.ignore();
}
while(board[position - 1] != " ") {
std::cout << "Already filled please choose another number between 1~9!\n";
std::cin >> position;
}
}
It works but for some reason when I put in an input like 10, it would print Please choose a number BETWEEN 1~9! twice (for each digit) and if I input in for example "apple" it would print Please choose a NUMBER between 1~9! four times (for each character). How do i make it just print out the statement once?
Thank you!
Let me try to explain to you the problem. It is a little bit subtle and not that easy to understand. Both other answers adress only the obvious part.
Then, let us first recap that:
The boolean condition in the while statement is loop invariant. Meaning, it will not be modified within the loop. Whatever it was before the loop, will be the same after the loop body has been executed. The condition will never change.
So, for the case where you enter a wrong number:
If the input number is correct (1..9) and the while statement starts to evaluate the boolean expression, it will be false in this case and the loop will not be entered.
If the number is out of your selected bounds (<1 or >9), then the boolean condition is true. The while loop starts, but the condition relevant variable will not be changed in the loop boody and hence, the boolean expression is always true. The loop will run forever.
Additionally, and now comes the answer to your first question, the following will happen:
The text "Please choose a number BETWEEN 1~9!\n" will be shown (first time)
clear will be called for std::cin. The failbit was not set, but anyway. This does not harm
The ignore function is an unformatted input function. It will actively read the next character from the input buffer, which is the end of line `'\n' character.
We enter again the while statement. The condition is still true (position was not modified in the loop body), and we enter the loop again.
The text "Please choose a number BETWEEN 1~9!\n" will be shown (second time)
clear will be called for std::cin. The failbit was not set, but anyway. This does not harm
The ignore function is a formatted input function. It will actively read the next character from the input buffer. But there is none. So it will wait until a key is pressed. For example "enter". After that, it would go back to number 5.
By the way. If you would now enter "abc" then you would see the text 4 times for a,b,c and enter.
So, please remember: ignore is an input function!
Next. It is important to understand, that if you enter an unexpected value, like "apple" instead of "3", the formatted input function >> can do no conversion and sets the failbit. It will also not extract further wrong characters from the input stream (std::cinis a buffered stream). The characters that could not be converted are still in the buffer and wil be read next time.
Please read here about formatted/unformatted input. And especially read about the extraction operatpr >> here..
There you can read the following:
If extraction fails (e.g. if a letter was entered where a digit is expected), zero is written to value and failbit is set.
OK, understood. Then, what is going on here, if you enter "abc". Basically, the same as above.
Enter abc
The boolean condition !(std::cin >> position)will be evaluated to true, because an 'a' was read and cannot be converted to a number.
The std::cin's failbit will be set. The variable positionwill be set to 0.
"Please choose a NUMBER between 1~9!\n" will be shown
The failbit will be reset
Ignore will extract exactly the one wrong character and discard it
std::cin >> position`` will be called again and extract the next wrong character 'b'. 3., 4., 5., 6. will be done again. Until the last charcter in the buffer, the newline '\n' will be extracted. Then you may enter the next number.
The fix for that problem is simple:
ignore has a parameter, where you can specify, how many characters shall be ignored. So, not only one, but all until the end of line.
You should write:
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
This will ignore all bad input.
And to fix your whole program, you could write at the top:
while (!(std::cin >> position) or (position < 1 ) or ( position > 9)) {
std::cout << "Please choose a NUMBER between 1~9!\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
while(position < 1 || position > 9){
This while loop will continue running as long as position is less than 1 or greater than 9, that's what this says.
But there's nothing in the while loop itself that changes the value of position. For that simple reason, if position's value at this point is outside of the range of 1-9, this while loop will execute forever.
You always need to keep in mind The Golden Rule Of Computer Programming: your computer always does exactly what you tell it to do instead of what you want it to do. Here, you told your computer to execute this while loop as long as position is less than 1 or greater than 9, so this is what your computer will do until this is no longer the case.
You can change your code as this. With this you run your while loop ones for every input. Until you get the value as you like. If the value is as you wish, you get out of the loop with break
std::cout << "Please choose a NUMBER between 1~9!\n";
while(std::cin >> position){
std::cin.clear();
std::cin.ignore();
if(position < 1 || position > 9){
std::cout << "Please choose a number BETWEEN 1~9!\n";
}else{
break;
}
}

Seekg not behaving as expected

#include <string>
#include <iostream>
int main() {
std::string str;
char magic[9];
std::cin.read((char *)magic, sizeof(magic));
std::cin.seekg(0, std::ios::beg);
while (std::cin >> str) {
std::cout << str << std::endl;
}
}
my code contains implementation of seekg(0) fucntion on std::cin
it is not behaving as expected on some of the files
when run as
./a.out < filename
those files that it is not behaving as expected have property that they have number of characters(including endline characters and other white spaces) less than 9(9 is the number of characters we read from cin before seekg)
if the file contains more than 9 characters it is behaving as expected
for example:
123456789
will give output as
123456789
while file containing less than 9 characters will not give output
for example:
1234
will give no output
With a file of less than nine characters, you have already attempted to read past the end with your initial read. That means the eof (end of file) and fail flags have been set for the stream and, while seekg may reset eof, it does not reset fail (a).
You can check that by inserting:
cout << "eof/fail=" << cin.eof() << '/' << cin.fail() << '\n';
immediately before and after the seekg. For file sizes of 8, 9, and 10 respectively, you get:
eof/fail=1/1
eof/fail=0/1
eof/fail=0/0
eof/fail=0/0
12345678
eof/fail=0/0
eof/fail=0/0
123456789
You can see the first failure results in no output because the fail bit is still set. The second and third have output because it was never set (the output is the characters shown plus one newline).
To repair this, you can clear the fail bit simply by inserting the following before your seekg:
std::cin.clear();
Then running that code on the eight-character file gives:
eof/fail=1/1
eof/fail=0/0
1234567
showing that the clear has indeed cleared the fail bit.
You might also want to keep in mind that it's not a requirement for a stream to be seekable, especially if it's just coming in via standard input. You may find for certain sized files that you cannot seek back an arbitrary amount if you've read through a large chunk of the stream.
(a) For the language lawyers amongst us, Unformatted input functions (C++11 27.7.2.3/41, C++14 27.7.2.3/41 and C++17 30.7.4.3/41) all have essentially the same text on how seekg works (my emphasis):
After constructing a sentry object, if fail() != true, executes ...

cin >> val sometimes reads 0 depending on Ctrl-Z

I was trying to write a code in C++ in Windows using MinGW compiler, my code counts and prints the number of consecutive times a number occurs in a given set of input. The code is as follows:
#include <iostream>
int main()
{
int c_val = 0,val = 0,cnt = 1;
std::cin>>c_val;
while(std::cin>>val){
if(val==c_val)
cnt++;
else{
std::cout<<c_val<<" occurs "<<cnt<< " times"<<std::endl;
c_val = val;
cnt = 1;
}
}
std::cout<<val<<" occurs "<<cnt<<" times";
}
INPUT: 42 42 42 12 13 13 ^Z (press Enter)
OUTPUT:
42 occurs 3 times
12 occurs 1 times
0 occurs 2 times
But if I press Enter before ^Z then it looks like:
INPUT: 42 42 42 12 13 13 (press Enter) ^Z (press Enter)
OUTPUT:
42 occurs 3 times
12 occurs 1 times
13 occurs 2 times
I want to know why the variable val in my code stores 13, when I use ^Z key after pressing my return key and why does it store 0 if I give the ^Z key along with my input.
Here is what happens. I observed this using MinGW-w64 4.9.2. The behaviour was the same whether running the executable in a Windows console, or under Cygwin (but not using cygwin-mingw).
Pressing ^Z at the beginning of a line sets the end-of-file condition
Pressing ^Z anywhere else actually sends ASCII 26 character to the stream
I also observed:
cin >> val sets val to 0 if it fails due to the input not containing a number.
cin >> val leaves val unchanged if input fails due to end-of-file.
According to this thread that is the correct behaviour specified by C++11.
So your results can be explained. When you input 42 42 42 12 13 13^Z, it is the same as if you had written 42 42 42 12 13 13x. The first six numbers are read, and then when x is encoutered, cin >> val fails and sets val to 0.
But when you press Enter and then ^Z, it is as if you were reading from a file and you reached the end of the file. cin >> val leaves val unchanged and it is still holding the value that it had after the last successful cin >> val.
If you make the change suggested by Gautam Jha suggested then you will get 13 in both cases. This is because he effectively reads into a temporary int, and then only stores the temporary int into the real val if the read succeeded, thereby avoiding the behaviour where a failed read sets val to 0.
This is probably the desired behaviour although you might also want to check cnt > 0 to avoid a weird output in the case of totally empty input.
See the difference
#include <iostream>
int main()
{
int c_val = 0,val = 0,cnt = 1;
std::cin>>c_val;
int curr_val = 0;
while(std::cin>>val){ // in case of no value cin will set val =0
curr_val = val;
if(curr_val == c_val)
cnt++;
else{
std::cout<<c_val<<" occurs "<<cnt<< " times"<<std::endl;
c_val = curr_val;
cnt = 1;
}
}
std::cout<<curr_val<<" occurs "<<cnt<<" times";
}

Unexpected behaviour of getline() with ifstream

To simplify, I'm trying to read the content of a CSV-file using the ifstream class and its getline() member function. Here is this CSV-file:
1,2,3
4,5,6
And the code:
#include <iostream>
#include <typeinfo>
#include <fstream>
using namespace std;
int main() {
char csvLoc[] = "/the_CSV_file_localization/";
ifstream csvFile;
csvFile.open(csvLoc, ifstream::in);
char pStock[5]; //we use a 5-char array just to get rid of unexpected
//size problems, even though each number is of size 1
int i =1; //this will be helpful for the diagnostic
while(csvFile.eof() == 0) {
csvFile.getline(pStock,5,',');
cout << "Iteration number " << i << endl;
cout << *pStock<<endl;
i++;
}
return 0;
}
I'm expecting all the numbers to be read, since getline is suppose to take what is written since the last reading, and to stop when encountering ',' or '\n'.
But it appears that it reads everything well, EXCEPT '4', i.e. the first number of the second line (cf. console):
Iteration number 1
1
Iteration number 2
2
Iteration number 3
3
Iteration number 4
5
Iteration number 5
6
Thus my question: what makes this '4' after (I guess) the '\n' so specific that getline doesn't even try to take it into account ?
(Thank you !)
You are reading comma separated values so in sequence you read: 1, 2, 3\n4, 5, 6.
You then print the first character of the array each time: i.e. 1, 2, 3, 5, 6.
What were you expecting?
Incidentally, your check for eof is in the wrong place. You should check whether the getline call succeeds. In your particular case it doesn't currently make a difference because getline reads something and triggers EOF all in one action but in general it might fail without reading anything and your current loop would still process pStock as if it had been repopulated successfully.
More generally something like this would be better:
while (csvFile.getline(pStock,5,',')) {
cout << "Iteration number " << i << endl;
cout << *pStock<<endl;
i++;
}
AFAIK if you use the terminator parameter, getline() reads until it finds the delimiter. Which means that in your case, it has read
3\n4
into the array pSock, but you only print the first character, so you get 3 only.
the problem with your code is that getline, when a delimiter is specified, ',' in your case, uses it and ignores the default delimiter '\n'. If you want to scan that file, you can use a tokenization function.