C++ IO and Formatting
- Description: A note on C++ I/O streams, formatting manipulators,
std::bindwith placeholders, and modern formatting (std::format,std::print) - My Notion Note ID: K2A-B1-20
- Created: 2018-12-27
- Updated: 2026-02-28
- License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io
Table of Contents
- 1.
std::ios_base::openmode - 2. Stream Formatting:
std::setwandstd::setfill - 3.
std::bindandstd::placeholders - 4.
std::formatandstd::print(C++20/23)
1. std::ios_base::openmode
When opening file streams, you can combine these flags to control how the file is opened:
| Flag | Meaning |
|---|---|
std::ios_base::in |
Open for reading |
std::ios_base::out |
Open for writing |
std::ios_base::app |
Seek to end of stream before each write (append) |
std::ios_base::ate |
Seek to end of stream immediately after opening |
std::ios_base::trunc |
Discard existing contents when opening |
std::ios_base::binary |
Open in binary mode (no text translation) |
#include <fstream>
// Open for reading and writing in binary mode
std::fstream file("data.bin",
std::ios_base::in | std::ios_base::out | std::ios_base::binary);
// Open for appending (does not truncate existing content)
std::ofstream log("app.log", std::ios_base::app);
// Open for writing, truncating existing content (default for ofstream)
std::ofstream out("output.txt", std::ios_base::out | std::ios_base::trunc);
2. Stream Formatting: std::setw and std::setfill
The <iomanip> header provides manipulators for controlling output formatting. std::setw sets the minimum field width, and std::setfill sets the padding character.
#include <iostream>
#include <iomanip>
int main() {
int value = 42;
// Pad with zeros to a width of 9 characters
std::cout << std::setw(9) << std::setfill('0') << value << std::endl;
// Output: 000000042
// Right-align with spaces (default)
std::cout << std::setw(9) << std::setfill(' ') << value << std::endl;
// Output: 42
// Left-align
std::cout << std::left << std::setw(9) << std::setfill('.') << value << std::endl;
// Output: 42.......
return 0;
}
Note: std::setw is reset after each insertion. std::setfill persists until changed.
Other commonly used <iomanip> manipulators:
std::setprecision(n)— Number of digits for floating-point output. Combine withstd::fixedorstd::scientificto control hownis interpreted.std::hex,std::oct,std::dec— Integer base for output and input.std::boolalpha/std::noboolalpha— Print booleans astrue/falseor1/0.std::showbase— Prefix0x/0on hex / octal output.
3. std::bind and std::placeholders
std::bind creates a new callable by binding some arguments of an existing callable to fixed values, while leaving others as placeholders for future arguments.
3.1 std::placeholders
Placeholders (_1, _2, _3, …) mark the positions where future arguments will be substituted. _1 represents the first argument passed to the bound function, _2 the second, and so on. Numbering starts at _1.
3.2 std::bind
std::bind produces a forwarding call wrapper. Calling the wrapper invokes the original function with the bound arguments substituted in.
#include <functional>
#include <iostream>
int sum(int a, int b) {
return a + b;
}
int main() {
using namespace std::placeholders;
// Bind the second argument to 3; _1 is the first argument passed to add_3
auto add_3 = std::bind(sum, _1, 3);
std::cout << add_3(4) << std::endl; // sum(4, 3) = 7
// Swap argument order
auto swapped = std::bind(sum, _2, _1);
std::cout << swapped(10, 20) << std::endl; // sum(20, 10) = 30
return 0;
}
3.3 Modern Alternative: Lambdas
In most cases, lambdas are clearer and more flexible than std::bind:
// Equivalent to std::bind(sum, _1, 3)
auto add_3 = [](int x) { return sum(x, 3); };
Lambdas are generally preferred in modern C++ because:
- They are easier to read at a glance.
- They behave more predictably with overloaded functions (no ambiguity in resolving which overload to bind).
- They typically optimize better — the compiler can inline through them, while
std::bindoften involves type-erased dispatch. - They support generic captures (
[](auto x) { ... }) and capture-by-move ([p = std::move(ptr)] { ... }), whichstd::bindcannot do cleanly.
4. std::format and std::print (C++20/23)
std::format (C++20, <format>) and std::print (C++23, <print>) are the modern alternatives to << and printf.
#include <format>
#include <print> // C++23
std::string s = std::format("Hello, {}!", "world"); // "Hello, world!"
std::print("count = {}\n", 42); // C++23: print to stdout
std::println("widget at ({}, {})", 1, 2); // adds a newline
std::format_to(buf, "pid {}", pid); // write to an iterator
Compared with the alternatives:
printf |
<< (iostream) |
std::format |
|
|---|---|---|---|
| Type-safe | No (format-string mismatch is runtime UB) | Yes | Yes |
| Custom types | Manual | Overload << |
Specialize std::formatter<T> |
| Speed | Fast | Slow | Fast |
| Positional args | No | No | Yes ({0}, {1}) |
| Compile-time format check | No | N/A | Yes (C++20) |
| Locale-independent | Yes | Mixed | Yes (default) |
For full coverage of format specifiers, custom formatters, and number/string conversions, see C++ Strings and Text § 5.