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";
}