Reading
Sections 6.1-6.2 of Problem Solving with C++.
Lecture
cinThe now-familiar cin object has type istream.
It is, in fact, the only object of that type we've seen up 'til now. Other than
the >>
operator, we haven't used it like we've used other types - we never try any
other operations. Oddly enough, objects of type istream can be cast as type bool,
which means for example that they can be used in the test condition of a loop
or an if-statement. But what
does that mean?!?!? Lets
look at an example:
int j, k; k = 0;while(cin >> j){ k = k + j;}cout << k << endl; |
Now what does this do? Well, if I run this program and enter
1 2 3 4;
... it'll print out 10. What it's done is to compute the sum of the numbers entered ... but why?
Well,
when cin
(or any other object of type istream) fails to read in an object of the type
you've requested, it goes into a state that as a bool is false. Otherwise, it translates into the
bool true.
So, in our loop, each time we read in a new int successfully with cin >> j the evaluated expression
(which is just cin
after the read) is of type istream and is cast to the bool value true - meaning the loop continues.
However, after reading the 4, the loop condition for the next iteration has cin
trying to read the character ';'
and interpret it as an int,
which it can't do. The read fails, so when we cast to the bool value we get false, and the loop terminates. In fact,
in this state any subsequent attempts to read will fail. This is a cool trick.
Sometimes
we want to read our input from a file rather than from standard-in. In this
case, cin
won't help us. However, we can create an istream object that acts just like
cin
except that it gets its character stream from a file rather than a keyboard.
So, let's do the same program as above, but first let's create a file temp with the input for our program.
#include <iostream>#include <fstream>using namespace std;int main() { // Create istream object that reads from file temp ifstream fin("temp"); // Loop and add as long as more int's can be read int j, k; k = 0; while(fin >> j) { k = k + j; } // Print out sum of all int's read cout << k << endl; return 0;} |
A
few comments:
When I say ifstream fin; it looks like I'm declaring an
object of type ifstream,
and yet I claim to be creating an object of type istream, which is it? Well ...
it's both. As you get into more advanced programming, the idea of subtypes
will be important. For now I'll just say this: cin and the fin we created are both of type istream
in the same way that "square" and "trapazoid"
are both in the category "quadrilateral" - they are subtypes of the
type quadrilateral.
Now,
if you run this, it's only going to successfully open and read from the file temp if temp is in the current directory. When I launch
a program it has a current directory in which it looks for files that
get referenced from within the program. When you run a program in Linux, its
current directory defaults to the current directory of your terminal (normally
for you this will be the same directory as where your source code and
executable is).
So
what happens when your program doesn't find the file it's looking for? Well,
when you create fin to read
from temp, and temp does not exist, then fin is in a state which, when cast as a bool, is
false. Thus, if we want to improve the program above so that it prints an error
message if the file temp
cannot be found, we would do the following:
// Create istream object that reads from file temp ifstream fin("temp"); if (fin) { // Found the file, read input & do work! } else { // Couldn't find the file! Report error! } |
When
reading data from a file, we often want to simply read until a file ends. Since
there isn't really an obvious analogue to that when reading from the keyboard,
we haven't had to deal with anything like this. The idea is this, if you try to
read an object, say an int
for example, and the file ends before istream object can read an int, the
istream
object goes into an error state again - i.e. casting it to a bool
would result in the value false. So, if you had a file infile that simply consisted of a
bunch of numbers, nothing else, you could sum these numbers with the following
code:
// Open ifstream to "infile" ifstream fin("infile"); // Read & sum each number in file int k, sum; sum = 0; fin >> k; while (fin) { sum = sum + k; fin >> k; } // Print out result cout << sum << endl; |
If
you prefer, we can use some of our shortcuts, namely that fin >> k has the side effect of
reading a new value into k,
as well as being an expression that has the value of the object fin after the new value is read
into k.
// Open ifstream to "infile" ifstream fin("infile"); // Read & sum each number in file int k, sum; sum = 0; while (fin >> k) sum = sum + k; // Print out result cout << sum << endl; |
We
know how to create an ifstream
object that is open for reading to a file. How are input file streams closed?
Well, when the ifstream
object goes out of scope that variable dies, and this closes the connection to the
file. If another ifstream
object is created to read from the same file, it starts over at the beginning.
Just
as reading from a file is not much different from reading from cin,
writing to a file is not much different from writing to cout. First, you need to create
an object of type ostream
like cout,
but which sends its output to a file. To do this, you declare a variable of
type ofstream.
#include <iostream>#include <fstream>using namespace std;int main() { // Create ostream object that writes to file outfile ofstream fout("outfile"); // Write 1 through 10 on separate lines int i; i = 1; while(i <= 10) { fout << i << endl; i = i + 1; } return 0;} |
You
don't really have the issue of checking to see if the file is found, since
you're trying to create a new file. If there is file of that name already
there, it gets obliterated ... oops! A fully featured program might warn the
user of this. Can you think of how you might check whether the file outfile already
exists?
If
name is a string object with the name of the file
you'd like to open, then
ifstream fin(name.c_str());
opens an input stream to that file, not ifstream (name), like you'd hope. The following
also works:
ifstream fin;fin.open(name.c_str());
The
deal is this: the iostream
stuff expects a C-style string, not a C++ string
object. when you have a C++ string object s, s.c_str() evaluates to the C-style version of
the same string.
1. Census Statistics - The
census keeps tables of
populations and population densities for all of our states. Each state has
its own file giving the names of all cities, towns, and CDP's ("census
designated place" - this appears to be census-eese
for "other") in that state. For example, take a look at Maryland's
geographic census data.
o The number of
towns in Maryland
o The number of
towns, cities and CDP's in Maryland
o The number of
towns, cities and CDP's in a user input state
Last modified LT M. Johnson: 08/15/2007 04:32 PM