flush and i/o buffering

Here are two examples of strange behavior:

#include <iostream>
using namespace std;

int main()
{
	cout << "Got here ...";
	int* A = 0;
	A[0] = 25;
	cout << " and now I'm here!" << endl;
	return 0;
}
When I run this, I expect to see Got here ..., and then see a Seg Fault message. Here's what I get instead:
~/$ ./prog 
Segmentation fault (core dumped)

Q: What happened to the "Got here" message?


#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
  for(int i = 0; i < 10; i++)
  {
    cout << '*';
    sleep(1);
  }
  cout << endl;
  return 0;
}
When I run this, I expect to see a *, then one second later see another *, then one second later see another *, and so on until 10 *'s are printed.

Q: Instead, I see nothing for 10 whole seconds, then all at once I see 10 *'s. What's happening?

The answer in both cases is I/O bufferring is happening. When you write things with cout, the characters you write are "bufferred", that means stored temporarily until either enough have been collected to make writing to the screen worthwhile, or some event has occurred to cause the less-than-full buffer to be written to the screen despite having unused capacity. Such events include:

We've seen these events but the last one. The last event "flushes the buffer", i.e. forces the contents of the buffer to be written to the screen, without writing a newline. Using this, we can get the second example working as we'd like:

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
  for(int i = 0; i < 10; i++)
  {
    cout << '*' << flush;
    sleep(1);
  }
  cout << endl;
  return 0;
}

Reading from unformatted input: cin.get() and eof

When we read in a value to variable x with the statement
cin >> x;
cin skips over leading whitespace in the input stream, meaning it skips over newline characters ('\n'), tabs ('\t'), and spaces (' '). This is a bit of a problem if you want to do something like count the number of lines in a file. You can read input a char at a time without skipping over whitespace using cin.get(), and you can read from ifstream object fin a char at a time without skipping over whitespace using fin.get().

So, suppose you wanted to read in a line of text from the user and print out how many characters were in the line, you'd write this:

#include <iostream>
using namespace std;

int main()
{
  int count = 0;
  for(char c = cin.get(); c != '\n'; c = cin.get())
    count++;
  cout << "There were " << count << " characters!" << endl;
  return 0;
}
Suppose fin is an ifstream object that you're reading from (remember to include fstream if you want to read and write with files). You can test whether or not you've come to the end of the file with the expression fin.eof() which evaluates to true if you have tried to read beyond the end of the file, and false otherwise.

So if you wanted to write a program to count the number of characters in the file Input.txt, you'd do something like this:

source code Input.txt output
#include <iostream>
using namespace std;

int main()
{
  // Open file stream
  ifstream FIS("Input.txt");

  // Read char c until the end of file
  int count = 0;

  for(int c = FIS.get(); !FIS.eof(); c = FIS.get())
  {
    cout << count << ":" << c << "(" << char(c) << ")" << endl;
    count++;
  }

  cout << "There were " << count << " characters!" << endl;

  return 0;
}
hello
0:104(h)
1:101(e)
2:108(l)
3:108(l)
4:111(o)
5:10(
)
There were 6 characters!
Note that the last character in Input.txt is a newline (ASCII code 10).
Caveats

The behavior fin.eof() is somewhat complex: it will return true when some previous read action failed because the end of file has been reached. In other words,

So, if you have just read the very last character in the file successfully fin.eof() will return false! If you try to read one more time, then fin.eof() will return true.

Problems

  1. Here's a good problem to work on, as it takes into account a number of the things we've talked about: The file scores.txt contains the scores of students on various problems on an exam. Each row corresponds to a student, and the scores along that row are that student's scores on problems 1, 2, 3 etc.
    Your job: figure out which problem was the hardest! You may assume that for every problem, at least one student got full credit for that problem. If the average score for problem X as a percentage of the full credit score for X is less than the average score for problem Y as a percentage of the full credit score for Y, then problem X is "harder" than problem Y.
    
    ~/$ ./prob1
    Problem p4 is hardest (ave = 48.5294%)
    Check out this solution.
  2. [NOTE -- the best approach to this problem requires pass-by-reference (and the given solutions use that) -- see the upcoming class on that] Use the file letters.txt and write a program that prints out the "NAVY" horizonatally. I.e. we want it to run like this:
    $ ./prog
    #.    #      #      #     #   #\   /#
    # #   #     # #      #   #     #\_/# 
    #  `# #    #===#      # #        #   
    #    `#   #     #      #         #   
    Here is the solution.

    A further challenge is to print out "NAVY" twice on the same line, like this:

    $ ./prog
    #.    #      #      #     #   #\   /#   #.    #      #      #     #   #\   /#
    # #   #     # #      #   #     #\_/#    # #   #     # #      #   #     #\_/# 
    #  `# #    #===#      # #        #      #  `# #    #===#      # #        #   
    #    `#   #     #      #         #      #    `#   #     #      #         #   
    Why is this interesting? Because the challenge here is to design the right "print" function. We can't print a whole letter at once now, because printing, for example, the first "N" requires newlines, and that would mess up printing the subsequent letters. Part of the Art of Programming is to make good choices about what functions to make, and exactly what they should do.

    If you're feeling really adventurous, I have a file alphabet.txt in the same format, but with all the letters of the alphabet. You could write a program that takes a message from the user and prints it out in the big letters. Something like this:

    ~/$ ./prog
    Enter message (all lower-case, no spaces): beatarmy
    
    
    ######\   #######      #      #######      #      ######    #\   /#   #\   /#
    #_____/   #____       # #        #        # #     #     #   # # # #    #\_/# 
    #     \   #          #===#       #       #===#    ######    #  *  #      #   
    ######/   #######   #     #      #      #     #   #     #   #     #      #