Show Lecture.IO as a slide show.
CS253 I/O
I/O Class Hierarchy
┌─────┐
│ ios │
└─────┘
△
│
┌────────────────┴────────────────┐
│ │
┌────┴────┐ ┌────┴────┐
│ istream │ │ ostream │
└─────────┘ └─────────┘
△ △
│ │
┌──────┴───────┐ ┌──────┴───────┐
│ │ │ │
┌─────┴────┐ ┌───────┴───────┐ ┌───────┴───────┐ ┌────┴─────┐
│ ifstream │ │ istringstream │ │ ostringstream │ │ ofstream │
└──────────┘ └───────────────┘ └───────────────┘ └──────────┘
The ⭡ indicates inheritance.
More
┌─────┐
│ ios │
└─────┘
△
│
┌────────────────┴────────────────┐
│ │
┌────┴────┐ ┌────┴────┐
│ istream │ │ ostream │
└─────────┘ └─────────┘
△ △
│ │
┌──────┴───────┐ ┌──────┴───────┐
│ │ │ │
┌─────┴────┐ ┌───────┴───────┐ ┌───────┴───────┐ ┌────┴─────┐
│ ifstream │ │ istringstream │ │ ostringstream │ │ ofstream │
└──────────┘ └───────────────┘ └───────────────┘ └──────────┘
ios
(input/output stream) is the base class
istream
and ostream
are subclasses of ios
- An
istream
is-a ios
- An
ostream
is-a ios
ifstream
and istringstream
are subclasses of istream
- An
ifstream
is-a istream
- An
istringstream
is-a istream
ofstream
and ostringstream
are subclasses of ostream
- An
ofstream
is-a ostream
- An
ostringstream
is-a ostream
Methods & Operator Overloading
┌─────┐
│ ios │
└─────┘
△
│
┌────────────────┴────────────────┐
│ │
┌────┴────┐ ┌────┴────┐
│ istream │ │ ostream │
└─────────┘ └─────────┘
△ △
│ │
┌──────┴───────┐ ┌──────┴───────┐
│ │ │ │
┌─────┴────┐ ┌───────┴───────┐ ┌───────┴───────┐ ┌────┴─────┐
│ ifstream │ │ istringstream │ │ ostringstream │ │ ofstream │
└──────────┘ └───────────────┘ └───────────────┘ └──────────┘
- What class defines
operator<<
?
- What class defines
operator>>
?
- What class defines
.open()
?
- What class defines
.get()
?
What Header Files Define What
strange type?
Consider the error message from this bad code:
cout.zork();
c.cc:1: error: 'std::ostream' {aka 'class std::basic_ostream<char>'} has no
member named 'zork'
- What’s this “
std::basic_ostream<char>
” business?
- Isn’t
cout
an object of type ostream
?
basic_ostream
- Isn’t
cout
an object of type ostream
?
- No,
cout
is an object of type basic_ostream<char>
.
- However:
typedef basic_ostream<char> ostream;
- Similarly:
typedef basic_istream<char> istream;
- And:
typedef basic_string<char> string;
Why!?
w-streams
- This is not simply generality for the same of generality.
- A
string
is a sequence of char
values;
a wstring
is a sequence of wchar_t
values.
- An
ostream
is an output stream of char
values;
a wostream
is an output stream of wchar_t
values.
- An
istream
is an input stream of char
values;
a wistream
is an input stream of wchar_t
values.
wchar_t
- What’s a
wchar_t
? It’s a wide character.
- Despite its peculiar name,
wchar_t
is a fundamental type,
like char
or int
.
- Its purpose is to hold a wide character, like ⻥ or 🐟,
that doesn’t fit into a one-byte
char
.
char c = 'X';
wchar_t wc = L'⻥';
cout << sizeof(c) << '\n'
<< sizeof(wc) << '\n';
1
4
basic_whatever<type>
- How would my students have implemented all this? 💖 💖 💖
- They’d have written
ostream
, then copied to ostream
code
to a new wostream
class, changing char
to wchar_t
.
- The code would then diverge as they fixed bugs in
ostream
but not in wostream
.
- After learning about templates, however, they would implement
the templated class
basic_ostream
, which takes either
char
or wchar_t
as a template argument.
Keep it DRY!
- Same for
ostream
and string
.
Unformatted output
We’re familiar with formatted I/O using <<
or >>
:
cout << "π ≈ " << 355/113.0 << endl;
π ≈ 3.14159
There’s also unformatted output:
cout.put('h');
cout.put('i');
char data[] = " there\nextra";
cout.write(data, 7);
hi there
Unformatted input
Similarly, there is unformatted input:
ifstream in("/etc/resolv.conf");
string s;
getline(in, s);
cout << "First: " << s << '\n';
char c;
while (in.get(c)) {
if (c == '.') c = '*';
cout << c;
}
First: search cs.colostate edu colostate.edu
nameserver 129*82*45*181
nameserver 129*82*103*78
nameserver 129*82*103*79
.unget()
undoes the most recent .get()
.
.peek()
is sort of like .get()
followed by .unget()
.
.ignore()
reads until a delimiter.
seek/tell
- An
ostream
has a put pointer, an integer value that keeps track
of how far along in the output we are.
- Similarly, an
istream
has a get pointer.
- You can query & change these:
- «ostream::tellp(cpp)»/«istream::tellg(cpp)»: return value of put/get pointer
- «ostream::seekp(cpp)»/«istream::seekg(cpp)»: change the put/get pointer
ofstream out("xyz");
out.write("Hxllo there\n", 11);
out.seekp(1);
out.put('e');
out.close();
cout << ifstream("xyz").rdbuf();
Hello there
seek/tell methods
- tell ≡ ask a question about location
- seek ≡ change location
- g ≡ discuss the input (get) pointer
- p ≡ discuss the output (put) pointer
tellg()
: return location of input stream
tellp()
: return location of output stream
seekg()
: change location of input stream
seekp()
: change location of output stream
Misuse of .open()
ifstream
and ofstream
both have .open()
methods:
ifstream in;
const string home = getpwnam("cs253")->pw_dir;
in.open(home+"/pub/ducks");
char c;
while (in.get(c))
cout << c;
Huey (red)
Dewey (blue)
Louie (green)
Why have that extra step? Just associate the filename at object construction:
const string home = getpwnam("cs253")->pw_dir;
ifstream in(home+"/pub/ducks");
char c;
while (in.get(c))
cout << c;
Huey (red)
Dewey (blue)
Louie (green)
Misuse of .eof()
- People who program by guesswork are often wrong about
.eof()
.
- They think that it means “Will the next read fail?”.
- They are wrong. It means “Did the previous read fail?”.
ifstream in("/etc/resolv.conf");
string line;
while (!in.eof()) {
getline(in, line);
cout << line << '\n';
}
search cs.colostate edu colostate.edu
nameserver 129.82.45.181
nameserver 129.82.103.78
nameserver 129.82.103.79
Where did that extra line come from?
The right way to detect end-of-file
Don’t inquire in advance—just go ahead and read. It will succeed or fail:
ifstream in("/etc/resolv.conf");
string line;
while (getline(in, line))
cout << line << '\n';
search cs.colostate edu colostate.edu
nameserver 129.82.45.181
nameserver 129.82.103.78
nameserver 129.82.103.79
Or, with fewer source lines and a reduced scope for line
:
ifstream in("/etc/resolv.conf");
for (string line; getline(in, line); )
cout << line << '\n';
search cs.colostate edu colostate.edu
nameserver 129.82.45.181
nameserver 129.82.103.78
nameserver 129.82.103.79