Chapter 6 Notes

Please read chapter 6 of the textbook.

Files

input and output, I/O for short, is a very important and practical topic

in this course, we’ll look at how you can use C++ streams to read and write files

basically, a file is a named sequence of bytes that is stored on a disk, or some other storage device

the operating system (OS) of your computer is responsible for managing files (and directories)

while every file can be viewed as a sequence of bytes, they often have some other structure, e.g. a file of doubles, a text file, an image file, etc.

C++ Streams

we’ll be using C++’s stream functions to read and write files

conceptually, a stream is a “flow” of data

you could have a stream of chars, or doubles, or strings, etc.

there are input streams for getting data from a file

and output streams for putting data into a file

for instance, cout is an output stream

and cin is an input stream

C++ Streams

cin and cout are examples of streams

more generally, they are examples of objects

i.e. streams are objects

in C++, objects are a related collection of data and functions

in later courses we’ll learn much more about objects

but for now, it’s useful to know that cin and cout are pre-defined objects

C++ Streams

reading and writing files with C++ streams is very similar to using cin and cout

we will create special streams attached to files, and then use the >> and << operators to read and write data with files

Reading and Writing Text Files with Streams

the following program sums the numbers in a text file

// sum_file.cpp

#include "cmpt_error.h"
#include <iostream>
#include <fstream>

using namespace std;

int main() {
    // open test_doubles.dat for reading
    // it is assumed to be a text file containing doubles
    ifstream fin("test_doubles.dat");

    if (fin.fail()) {
        cmpt::error("couldn't open file");
    }

    double sum = 0.0;
    double x = 0.0;
    while (fin >> x) {
        sum += x;
    }

    // create an output file to write the output to
    ofstream fout("sum_out.txt");
    if (fout.fail()) {
        cmpt::error("couldn't open file for fout");
    }

    fout << "sum = " << sum << "\n";
}

fstream is where the file I/O streams are contained

ifstream is the data type for a file input stream

ofstream is the data type for a file output stream

notice how fin and fout are used in pretty much the same was as cin and cout

that’s by design — the C++ stream library tries to make all input and output work this way

Reading and Writing Text Files with Streams

note how we check fin.fail() and fout.fail() to see if the files have been successfully opened

fin and fout are examples of objects

fail() is an example of a member function

member functions are functions that belong to objects

member functions are called using dot-notation, e.g. fin.fail() means “call the member function in the object fin named fail()

Some Notes on the Textbook

the textbook suggests defining string variables like this

char file_name[16];  // a C-style string that can hold 16 characters

this is how strings are declared in C

C-style strings have a fixed size, in this case 16 characters

file_name simply cannot hold more than 16 characters

but in C++, for application-level programs it is almost always better to just use the string class, e.g.

string file_name;

file_name can now be of any length

C-style strings are perhaps most useful for low-level applications, such as making your own string class (this is the sort of thing we’ll do in later courses)

Some Notes on the Textbook

the textbook suggests using the exit(code) function to end your program when you’ve encountered an error

however, it is better to use cmpt::error for two reasons

with cmpt::error, we provide an English message instead of a code, e.g. cmpt::error("file not found") is easier for people to read than exit(2) (what does 2 mean??)

the exit function does not throw an exception, which means it cannot be caught

in other words, when you call exit, your program is guaranteed to stop

if this is really what you want, then that’s good

but in most real-world programs, we don’t want a single error to crash the entire program

cmpt::error is different because it throws an exception

an exception is a special error object that other code in your program can, if it wants to, catch

we have not been catching our errors, but when we learn about try/catch structures we will see how we could do this

Formatted Streams

C++ streams come with a number of functions that can be used for formatting

see page 327 for a table showing the most commonly used functions

also, on page 329 the textbook discusses stream manipulators

you won’t be expected to memorize any of this in this course

just be aware of it and look it up when you need it

Character I/O

it’s often useful to read a file a character at a time

unfortunately, this approach does not work

char c;
while (fin >> c) {    // skips whitespace
    // process char c
}

the problem is that >> skips whitespace, so characters such as space, newline, and tab will not be processed by this code

Character I/O

you can use the get() function to process one character at a time

#include "cmpt_error.h"
#include <iostream>
#include <fstream>

using namespace std;

void main() {
    ifstream fin("test.txt");

    if (fin.fail()) {
        cmpt::error("couldn't open file");
    }

    int char_count = 0;
    char c = fin.get();  // must read at least one character
                         // for fin.eof() to work
    while (!fin.eof()) { // loop until end-of-file reached
        ++char_count;
        if (c == '\n') {
            cout << char_count << " '\\n'\n"; // note the double back-slashes
        } else {
            cout << char_count << " '" << c << "'\n";
        }
        c = fin.get();
    }
    cout << char_count << " characters\n";
}

fin.get() tries to read the next character of the fin stream

if there is a next character, then it is returned

if there is no next character, that means that the special end-of-file character has been read

it’s value is returned

and fin.eof() is true

Character I/O

it’s worth noting that that fin.get() does not return a char (!?)

instead, fin.get() returns an int

this way it can return a special non-character value to indicate EOF

if it did return a char, what char should it return when it reads the EOF?

Character I/O

here’s an example of how you might use character I/O

this code reads input from cin one character at a time

this includes spaces, so it is different than >>

cout << "Please enter some words: ";
string line;

char c = cin.get();
while (c != '\n') {  // stop after user presses enter
    line += c;
    c = cin.get();
}

the statement line += c appends to the character c to the string line

this is a quick and dirty way to create a new string a character at a time

we could also have written this as a for-loop

for(char c = cin.get(); c != '\n'; c = cin.get()) {
    line += c;
}
cout << "line = \"" << line << "\"\n";

while the for-loop header is complex, the benefit is that all the loop-control information is gathered in one place

Sample program: Average of Numbers Using Files

// temps.cpp

#include <iostream>
#include <fstream>
#include "cmpt_error.h"

using namespace std;

int main() {
    ifstream fin("meanTemps.txt");

    if (fin.fail()) {
        cmpt::error("couldn't open file");
    }

    // cout << "Please enter some temperatures: ";
    double sum = 0.0;
    int count = 0;
    double temp;
    while (fin >> temp) {
        sum += temp;
        count++;
    }
    fin.close(); // optional

    ofstream fout("log.txt");

    fout << count << " temperatures read\n";
    double mean = sum / count;
    fout << "Average = " << mean << "\n";
    cout << "Results printed to log.txt\n";
}

Sample program: Reading Character-by-Character

// readchar.cpp

#include <iostream>

using namespace std;

int main() {
    cout << "Please enter some text: ";
    string line;
    char c = cin.get();
    while (c  != '!') { // keep reading until first '!'
        line += c;
        c = cin.get();
    }

    // replace each newline character with the '\' and 'n'
    cout << "line=\"";
    for(char c : line) {  // note the for-each loop used here
        if (c == '\n') {
            cout << "\\n";
        } else {
            cout << c;
        }
    }
    cout << "\"\n";
}