본문으로 건너뛰기

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:

StreamMeaning
cinStandard input
coutStandard output
cerrStandard 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.

HeaderTypeMeaning
<iostream>istreamInput stream
<iostream>ostreamOutput stream
<iostream>iostreamInput and output stream
<fstream>ifstreamInput from a file
<fstream>ofstreamOutput to a file
<fstream>fstreamInput and output to a file
<sstream>istringstreamInput from a string
<sstream>ostringstreamOutput to a string
<sstream>stringstreamInput 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:

StateMeaning
goodbitNo error
eofbitEnd-of-file reached
failbitRecoverable IO failure
badbitSerious 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:

OperationMeaning
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.

ModeMeaning
std::ios::inOpen for input
std::ios::outOpen for output
std::ios::appWrite at the end before every output
std::ios::ateMove to the end immediately after opening
std::ios::truncDiscard existing contents
std::ios::binaryBinary 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:

TypeMeaning
istringstreamRead from a string
ostringstreamWrite to a string
stringstreamRead 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:

  1. read one line
  2. create an istringstream
  3. 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

  1. The IO library provides stream-based input and output.
  2. istream represents input operations.
  3. ostream represents output operations.
  4. cin is the standard input stream.
  5. cout is the standard output stream.
  6. cerr is a standard error stream.
  7. >> performs formatted input.
  8. << performs formatted output.
  9. getline() reads an entire line.
  10. <iostream> provides standard stream types.
  11. <fstream> provides file stream types.
  12. <sstream> provides string stream types.
  13. ifstream behaves as an input stream.
  14. ofstream behaves as an output stream.
  15. istringstream behaves as an input stream over a string.
  16. ostringstream behaves as an output stream over a string.
  17. Stream objects cannot normally be copied.
  18. Streams are therefore normally passed by reference.
  19. Stream parameters are usually non-const because IO modifies stream state.
  20. Streams maintain condition-state bits.
  21. goodbit means no IO error.
  22. failbit represents an IO operation failure.
  23. badbit represents a serious IO failure.
  24. eofbit indicates end-of-file.
  25. A stream can be tested directly as a condition.
  26. good(), fail(), bad(), and eof() inspect stream state.
  27. rdstate() returns the current state.
  28. clear() resets or replaces stream state.
  29. setstate() adds condition bits.
  30. Output streams normally use buffering.
  31. endl writes a newline and flushes.
  32. flush flushes without adding a character.
  33. ends writes a null character and flushes.
  34. unitbuf requests flushing after every output operation.
  35. nounitbuf restores normal buffering.
  36. cin is normally tied to cout.
  37. tie() inspects or changes a stream's tied output stream.
  38. ifstream reads files.
  39. ofstream writes files.
  40. fstream can perform file input and output.
  41. A file stream can open a file in its constructor.
  42. open() associates an existing stream object with a file.
  43. close() closes the currently associated file.
  44. is_open() tests whether a file is open.
  45. Opening failure sets the stream's failure state.
  46. A file stream automatically closes its file when destroyed.
  47. ifstream can be passed where an istream& is expected.
  48. ofstream can be passed where an ostream& is expected.
  49. in opens a file for input.
  50. out opens a file for output.
  51. Ordinary output mode normally truncates existing contents.
  52. app preserves existing data and writes at the end.
  53. ate moves to the end immediately after opening.
  54. trunc discards existing contents.
  55. binary selects binary IO mode.
  56. File modes can be combined with bitwise OR.
  57. File mode is chosen independently on each open() call.
  58. istringstream parses values from an in-memory string.
  59. ostringstream builds a string using stream output operations.
  60. stringstream supports both input and output on a string.
  61. str() returns the associated string.
  62. str(s) replaces the associated string.
  63. String streams are especially useful for parsing individual lines.
  64. The same istream or ostream interface can work with console, file, and string streams.