The IO Library
IO Library: A set of standard-library types used for input and output.
Stream: A sequence of characters transferred between a program and an input source or output destination.
The standard IO library provides several common streams:
| Stream | Meaning |
|---|---|
cin | Standard input |
cout | Standard output |
cerr | Standard error output |
Basic IO:
#include <iostream>
int main()
{
int value = 0;
std::cin >> value;
std::cout << value << '\n';
}
>> reads data from an input stream.
<< writes data to an output stream.
Reading a Line
getline() reads an entire line into a string.
#include <iostream>
#include <string>
int main()
{
std::string line;
std::getline(std::cin, line);
std::cout << line << '\n';
}
Unlike:
std::cin >> line;
which normally stops at whitespace, getline() reads until the newline.
8.1 The IO Classes
The standard library provides different stream classes depending on the IO source or destination.
| Header | Type | Meaning |
|---|---|---|
<iostream> | istream | Input stream |
<iostream> | ostream | Output stream |
<iostream> | iostream | Input and output stream |
<fstream> | ifstream | Input from a file |
<fstream> | ofstream | Output to a file |
<fstream> | fstream | Input and output to a file |
<sstream> | istringstream | Input from a string |
<sstream> | ostringstream | Output to a string |
<sstream> | stringstream | Input and output to a string |
Standard Stream Types
istream provides input operations.
std::istream& input = std::cin;
ostream provides output operations.
std::ostream& output = std::cout;
Because cin and cout are stream objects, they use the same interfaces as other compatible streams.
File Stream Types
Required header:
#include <fstream>
Input file:
std::ifstream input("data.txt");
Output file:
std::ofstream output("result.txt");
Input and output:
std::fstream file("data.txt");
String Stream Types
Required header:
#include <sstream>
Read from a string:
std::istringstream input("10 20 30");
Write into a string:
std::ostringstream output;
Read and write:
std::stringstream stream;
Wide-Character Streams
Wide-character stream types operate on wchar_t.
Examples include:
wistream
wostream
wifstream
wofstream
wistringstream
wostringstream
Their names generally begin with w.
Relationships among the IO Types
File and string streams reuse the interfaces of the basic stream types.
Conceptually:
istream
├─ ifstream
└─ istringstream
ostream
├─ ofstream
└─ ostringstream
Therefore a function accepting an istream& can also work with an ifstream.
void read_value(
std::istream& input,
int& value)
{
input >> value;
}
Both of these calls are valid:
int value = 0;
read_value(std::cin, value);
std::ifstream file("data.txt");
read_value(file, value);
The function works with either source because both provide the istream interface.
1) No Copy or Assign for IO Objects
IO stream objects cannot be copied.
Invalid:
std::ifstream input("data.txt");
// Error:
// std::ifstream copy = input;
Streams are therefore normally passed by reference.
void process(std::istream& input)
{
int value = 0;
input >> value;
}
Streams Are Usually Non-const
IO operations change stream state.
Therefore stream references are normally non-const.
Correct:
void read(
std::istream& input)
{
int value = 0;
input >> value;
}
Not:
// void read(
// const std::istream& input)
// {
// input >> value;
// }
Input changes the state of the stream, so the stream cannot be treated as const.
2) Condition States
Every stream maintains a condition state describing whether IO operations succeeded.
Important state bits include:
| State | Meaning |
|---|---|
goodbit | No error |
eofbit | End-of-file reached |
failbit | Recoverable IO failure |
badbit | Serious IO failure |
Stream as a Condition
A stream can be tested directly in a condition.
int value = 0;
while (std::cin >> value)
{
std::cout << value << '\n';
}
The expression:
std::cin >> value
returns the stream.
The stream is then tested as a condition.
The loop continues while input succeeds.
Input Failure
Suppose the program expects an integer:
int value = 0;
std::cin >> value;
Input:
hello
cannot be converted to int.
The stream enters a failed state.
After that, later input operations normally fail until the state is cleared.
Testing Stream State
Important operations include:
| Operation | Meaning |
|---|---|
s.good() | No error bits are set |
s.fail() | failbit or badbit is set |
s.bad() | badbit is set |
s.eof() | eofbit is set |
s.rdstate() | Returns current state |
s.clear() | Clears error state |
s.clear(flags) | Replaces current state |
s.setstate(flags) | Adds state bits |
Example:
if (std::cin.fail())
{
std::cout << "input failed\n";
}
good()
if (std::cin.good())
{
std::cout << "stream is valid\n";
}
good() is true only when no error state is present.
fail()
int value = 0;
std::cin >> value;
if (std::cin.fail())
{
std::cerr << "invalid input\n";
}
fail() is useful for detecting normal formatting or conversion errors.
eof()
if (std::cin.eof())
{
std::cout << "end of input\n";
}
It specifically checks whether end-of-file was encountered.
bad()
if (std::cin.bad())
{
std::cerr
<< "serious IO error\n";
}
badbit normally represents a more serious stream-level failure.
Managing the Condition State
rdstate() retrieves the current stream state.
auto state =
std::cin.rdstate();
clear() resets the stream state.
std::cin.clear();
After an input conversion failure:
int value = 0;
if (!(std::cin >> value))
{
std::cin.clear();
}
the error state is cleared.
Recovering from Invalid Input
Clearing the stream state alone does not remove the invalid input that caused the error.
A common recovery pattern is:
#include <iostream>
#include <limits>
int value = 0;
while (!(std::cin >> value))
{
std::cin.clear();
std::cin.ignore(
std::numeric_limits<
std::streamsize>::max(),
'\n'
);
std::cout
<< "Enter an integer: ";
}
Conceptually:
invalid input
↓
stream fails
↓
clear error state
↓
discard invalid characters
↓
try again
setstate()
Additional state bits can be set explicitly.
stream.setstate(
std::ios::failbit);
The stream will then behave as a failed stream until that state is cleared.
3) Managing the Output Buffer
Output is often stored temporarily in an output buffer before being written to the actual destination.
For example:
std::cout << "Hello";
does not necessarily mean the characters are immediately sent to the terminal.
They may remain buffered temporarily.
Why Buffer Output?
Buffering reduces the number of expensive IO operations.
Conceptually:
program output
↓
buffer
↓
terminal / file
Several small writes can be collected and written together.
Flushing the Output Buffer
Flush: Forces buffered output to be written to the destination.
C++ provides several manipulators.
endl
endl writes a newline and flushes the stream.
std::cout
<< "Hello"
<< std::endl;
Equivalent conceptually to:
write "Hello"
write '\n'
flush output
flush
flush flushes without writing another character.
std::cout
<< "Loading..."
<< std::flush;
This can be useful when the output must appear immediately.
ends
ends writes a null character and then flushes the stream.
std::cout
<< "Hello"
<< std::ends;
Unlike endl, it does not simply mean newline.
'\n' vs. endl
This:
std::cout << "Hello\n";
writes a newline without explicitly requesting a flush.
This:
std::cout
<< "Hello"
<< std::endl;
writes a newline and requests a flush.
Use endl when flushing is actually required.
unitbuf
unitbuf causes output to be flushed after every output operation.
std::cout
<< std::unitbuf;
After this:
std::cout << "A";
std::cout << "B";
each output operation is flushed automatically.
nounitbuf
Normal buffering can be restored using:
std::cout
<< std::nounitbuf;
Tied Streams
An input stream can be tied to an output stream.
Before input occurs, the tied output stream is flushed.
By default:
cin is tied to cout
Therefore:
std::cout
<< "Enter value: ";
int value = 0;
std::cin >> value;
the prompt normally appears before the program waits for input.
tie()
The current tied stream can be inspected.
std::ostream* tied =
std::cin.tie();
cin is normally tied to cout.
A stream can also be retied.
std::cin.tie(&std::cerr);
or untied:
std::cin.tie(nullptr);
8.2 File Input and Output
File Stream: A stream associated with a named file.
C++ provides:
ifstream input
ofstream output
fstream input + output
Required header:
#include <fstream>
1) Using File Stream Objects
A file stream must be associated with a file before file IO can occur.
Opening with the Constructor
std::ifstream input(
"data.txt");
The constructor attempts to open the file immediately.
Reading:
int value = 0;
input >> value;
Writing with ofstream
std::ofstream output(
"result.txt");
output
<< "Hello"
<< '\n';
The characters are written to the associated file.
Reading and Writing
std::fstream file(
"data.txt");
An fstream supports both input and output when opened with appropriate modes.
Creating an Unbound File Stream
std::ifstream input;
At this point the stream is not associated with a file.
Later:
input.open(
"data.txt");
binds it to a file.
is_open()
Check whether a file is open:
if (input.is_open())
{
std::cout
<< "file opened\n";
}
Test the Stream after Opening
Because failure to open a file sets the stream state, the stream itself can be tested.
std::ifstream input(
"data.txt");
if (!input)
{
std::cerr
<< "failed to open file\n";
}
A common full pattern is:
#include <fstream>
#include <iostream>
int main()
{
std::ifstream input(
"data.txt");
if (!input)
{
std::cerr
<< "cannot open data.txt\n";
return 1;
}
int value = 0;
while (input >> value)
{
std::cout
<< value
<< '\n';
}
}
Using an ifstream as an istream
Because ifstream provides the istream interface:
void process(
std::istream& input)
{
std::string word;
while (input >> word)
{
std::cout
<< word
<< '\n';
}
}
This function works with:
process(std::cin);
and:
std::ifstream file(
"data.txt");
process(file);
The function does not need to know whether the input comes from the keyboard or a file.
Using an ofstream as an ostream
void print(
std::ostream& output,
int value)
{
output
<< value
<< '\n';
}
Use with console:
print(std::cout, 10);
Use with file:
std::ofstream file(
"result.txt");
print(file, 10);
This is one important benefit of using stream interfaces.
open()
A stream can explicitly open a file.
std::ifstream input;
input.open(
"data.txt");
Opening failure sets the stream's failure state.
if (!input)
{
std::cerr
<< "open failed\n";
}
close()
A file can be explicitly closed.
input.close();
After closing it, the same stream object can later open another file.
std::ifstream input;
input.open("a.txt");
// process a.txt
input.close();
input.open("b.txt");
// process b.txt
Opening an Already-Open Stream
A file stream can normally be associated with only one file at a time.
Do not do:
std::ifstream input(
"a.txt");
// Fails:
// input.open("b.txt");
Close the first file:
input.close();
input.open("b.txt");
Automatic File Closing
A local file stream automatically closes its file when the stream object is destroyed.
void process_file()
{
std::ifstream input(
"data.txt");
// use input
}
When process_file() ends:
input destroyed
↓
file automatically closed
Explicit close() is needed mainly when the same stream must open another file before its lifetime ends.
Processing Multiple Files
#include <fstream>
#include <string>
#include <vector>
void process_files(
const std::vector<
std::string>& filenames)
{
for (const auto& name
: filenames)
{
std::ifstream input(
name);
if (!input)
{
continue;
}
std::string line;
while (
std::getline(
input,
line))
{
// process line
}
}
}
Each ifstream is destroyed at the end of the loop iteration and its file is closed automatically.
2) File Modes
A file mode controls how a file is opened.
| Mode | Meaning |
|---|---|
std::ios::in | Open for input |
std::ios::out | Open for output |
std::ios::app | Write at the end before every output |
std::ios::ate | Move to the end immediately after opening |
std::ios::trunc | Discard existing contents |
std::ios::binary | Binary IO mode |
Default Modes
ifstream normally opens with:
std::ios::in
ofstream normally opens with:
std::ios::out
fstream normally uses both input and output capabilities according to its default file-stream behavior.
Explicit Input Mode
std::ifstream input(
"data.txt",
std::ios::in);
Explicit Output Mode
std::ofstream output(
"result.txt",
std::ios::out);
Opening an output file in ordinary out mode normally discards existing contents.
trunc
Explicitly truncate the existing file:
std::ofstream output(
"result.txt",
std::ios::out |
std::ios::trunc);
Existing data is removed.
app
Append new data while preserving existing contents.
std::ofstream output(
"log.txt",
std::ios::app);
output
<< "new record\n";
Existing data remains.
New output is written at the end.
out vs. app
Suppose log.txt contains:
old data
Using:
std::ofstream output(
"log.txt");
output << "new data\n";
may replace the existing contents.
Using:
std::ofstream output(
"log.txt",
std::ios::app);
output << "new data\n";
produces conceptually:
old data
new data
ate
ate moves to the end once immediately after opening.
std::fstream file(
"data.txt",
std::ios::in |
std::ios::out |
std::ios::ate);
Unlike app, later positioning is not necessarily restricted to the end.
binary
Binary mode avoids text-mode transformations performed by some systems.
std::ifstream input(
"image.bin",
std::ios::binary);
It can be combined with other modes:
std::ofstream output(
"data.bin",
std::ios::out |
std::ios::binary);
Combining File Modes
Modes are combined with bitwise OR.
std::fstream file(
"data.txt",
std::ios::in |
std::ios::out);
This opens the file for both reading and writing.
Mode Is Selected Each Time open() Is Called
std::ofstream output;
output.open(
"a.txt",
std::ios::out);
output.close();
output.open(
"b.txt",
std::ios::app);
The first file uses ordinary output mode.
The second file uses append mode.
Each call selects its own mode.
8.3 string Streams
String Stream: A stream whose source or destination is an in-memory string.
Required header:
#include <sstream>
The main types are:
| Type | Meaning |
|---|---|
istringstream | Read from a string |
ostringstream | Write to a string |
stringstream | Read and write a string |
String streams allow normal stream operations without using a file or terminal.
Basic Operations
Create an empty string stream:
std::stringstream stream;
Create one from a string:
std::istringstream stream(
"10 20 30");
Retrieve the associated string:
std::string result =
stream.str();
Replace the associated string:
stream.str(
"new contents");
1) Using an istringstream
istringstream: Treats a string as an input stream.
Suppose:
std::string line =
"10 20 30";
Create a stream:
std::istringstream input(
line);
Then extract values normally:
int a = 0;
int b = 0;
int c = 0;
input >> a >> b >> c;
Results:
a = 10
b = 20
c = 30
Parsing a Line
One common pattern is:
- read one line
- create an
istringstream - parse individual fields
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::string line;
while (
std::getline(
std::cin,
line))
{
std::istringstream record(
line);
std::string name;
int age = 0;
double score = 0.0;
record
>> name
>> age
>> score;
std::cout
<< name << ' '
<< age << ' '
<< score
<< '\n';
}
}
Input:
Alice 20 95.5
Bob 22 88.0
Each line is parsed independently.
Parsing an Unknown Number of Values
std::string line =
"10 20 30 40";
std::istringstream input(
line);
int value = 0;
while (input >> value)
{
std::cout
<< value
<< '\n';
}
Output:
10
20
30
40
Reading eventually reaches the end of the associated string.
The stream then enters a failed/end state and the loop terminates.
Example: Record Parsing
struct Person
{
std::string name;
std::vector<int> numbers;
};
Suppose each line contains:
name number number number ...
Parsing:
std::string line;
while (
std::getline(
std::cin,
line))
{
std::istringstream record(
line);
Person person;
record >> person.name;
int number = 0;
while (record >> number)
{
person.numbers.push_back(
number);
}
}
The outer stream handles lines.
The inner string stream handles fields within each line.
Why istringstream Is Useful
Without it, parsing a line often requires manual string indexing and conversion.
With a string stream:
record >> name >> age >> score;
the normal stream extraction rules can be reused.
2) Using ostringstreams
ostringstream: Treats an in-memory string as an output stream.
Create one:
std::ostringstream output;
Write using normal output syntax:
output
<< "value = "
<< 42;
Retrieve the resulting string:
std::string text =
output.str();
Result:
value = 42
Building a Formatted String
#include <sstream>
#include <string>
std::string make_message(
const std::string& name,
int value)
{
std::ostringstream output;
output
<< "name: "
<< name
<< ", value: "
<< value;
return output.str();
}
Usage:
std::string message =
make_message(
"Grap",
42);
Result:
name: Grap, value: 42
Incremental String Construction
An ostringstream is useful when a string is constructed from several values.
std::ostringstream output;
output << "position = ";
output << '(';
output << 10;
output << ", ";
output << 20;
output << ')';
std::string text =
output.str();
Result:
position = (10, 20)
Replacing the Associated String
str() can also replace the stream's underlying string.
std::stringstream stream;
stream.str(
"10 20");
int a = 0;
int b = 0;
stream >> a >> b;
Results:
a = 10
b = 20
Retrieving the Associated String
std::ostringstream output;
output
<< "Hello "
<< 42;
std::string result =
output.str();
result contains:
Hello 42
Standard, File, and String Streams
The three major stream sources can be viewed as the same interface applied to different data sources.
istream interface
│
├─ cin
│ keyboard / standard input
│
├─ ifstream
│ file
│
└─ istringstream
string
Likewise:
ostream interface
│
├─ cout
│ terminal / standard output
│
├─ ofstream
│ file
│
└─ ostringstream
string
This allows functions to be written against the generic stream interfaces.
Example:
void write_result(
std::ostream& output,
int value)
{
output
<< "result = "
<< value
<< '\n';
}
Use with terminal:
write_result(
std::cout,
42);
Use with file:
std::ofstream file(
"result.txt");
write_result(
file,
42);
Use with string:
std::ostringstream buffer;
write_result(
buffer,
42);
std::string text =
buffer.str();
The same function can write to three different destinations.
Essential Study Checklist
- The IO library provides stream-based input and output.
istreamrepresents input operations.ostreamrepresents output operations.cinis the standard input stream.coutis the standard output stream.cerris a standard error stream.>>performs formatted input.<<performs formatted output.getline()reads an entire line.<iostream>provides standard stream types.<fstream>provides file stream types.<sstream>provides string stream types.ifstreambehaves as an input stream.ofstreambehaves as an output stream.istringstreambehaves as an input stream over a string.ostringstreambehaves as an output stream over a string.- Stream objects cannot normally be copied.
- Streams are therefore normally passed by reference.
- Stream parameters are usually non-const because IO modifies stream state.
- Streams maintain condition-state bits.
goodbitmeans no IO error.failbitrepresents an IO operation failure.badbitrepresents a serious IO failure.eofbitindicates end-of-file.- A stream can be tested directly as a condition.
good(),fail(),bad(), andeof()inspect stream state.rdstate()returns the current state.clear()resets or replaces stream state.setstate()adds condition bits.- Output streams normally use buffering.
endlwrites a newline and flushes.flushflushes without adding a character.endswrites a null character and flushes.unitbufrequests flushing after every output operation.nounitbufrestores normal buffering.cinis normally tied tocout.tie()inspects or changes a stream's tied output stream.ifstreamreads files.ofstreamwrites files.fstreamcan perform file input and output.- A file stream can open a file in its constructor.
open()associates an existing stream object with a file.close()closes the currently associated file.is_open()tests whether a file is open.- Opening failure sets the stream's failure state.
- A file stream automatically closes its file when destroyed.
ifstreamcan be passed where anistream&is expected.ofstreamcan be passed where anostream&is expected.inopens a file for input.outopens a file for output.- Ordinary output mode normally truncates existing contents.
apppreserves existing data and writes at the end.atemoves to the end immediately after opening.truncdiscards existing contents.binaryselects binary IO mode.- File modes can be combined with bitwise OR.
- File mode is chosen independently on each
open()call. istringstreamparses values from an in-memory string.ostringstreambuilds a string using stream output operations.stringstreamsupports both input and output on a string.str()returns the associated string.str(s)replaces the associated string.- String streams are especially useful for parsing individual lines.
- The same
istreamorostreaminterface can work with console, file, and string streams.