Phase 1 · Modern C++ FoundationsModule 3~46 min read

Console I/O, Strings & Formatting

Communicate through standard streams, work with std::string, format results, read complete lines, and recover from invalid input.

What you'll learn

Useful programs exchange data with people, files, and other programs. C++ streams provide a typed interface for that exchange, while std::string gives text safe value semantics and automatic storage management.

By the end, you'll be able to:

  • Route results and diagnostics through the correct standard stream
  • Create, combine, inspect, and compare std::string values
  • Choose between token extraction and complete-line input
  • Recover a failed input stream and parse structured text exactly

The standard streams

Streams model a sequence of characters plus state describing whether operations succeeded. The operating environment connects the standard C++ stream objects to three standard channels.

C++ standard character streams
stdin
std::cin
program input
stdout
std::cout
normal results
stderr
std::cerr
immediate diagnostics
stderr
std::clog
buffered diagnostics
streams.cpp
#include <iostream>

int main() {
    std::cout << "Result: " << 42 << '\n';
    std::clog << "Calculation completed\n";

    bool valid{false};
    if (!valid) {
        std::cerr << "Error: value is invalid\n";
        return 1;
    }
}

Key idea

Keep ordinary results on std::cout and diagnostics on std::cerr orstd::clog. Shell users can then redirect data and errors independently.

Output & formatting

The insertion operator << writes values from left to right. Manipulators from<iomanip> configure how later values are represented.

formatting.cpp
#include <iomanip>
#include <iostream>

int main() {
    double price{19.5};

    std::cout << std::fixed << std::setprecision(2);
    std::cout << "Price: $" << price << '\n';
    std::cout << std::left << std::setw(12) << "C++ course"
              << std::right << std::setw(6) << 30 << '\n';
    std::cout << std::boolalpha << (price < 20.0) << '\n';
}
ManipulatorEffect
std::fixedUse fixed-point floating notation
std::setprecision(n)Set fractional digits with fixed; significant digits otherwise
std::setw(n)Set the minimum width of the next field only
std::left / std::rightChoose field alignment
std::boolalphaWrite true/false instead of 1/0

Tip

Prefer '\n' for an ordinary newline. std::endl also flushes the stream, which is useful only when an immediate flush is actually required.

Working with std::string

std::string owns a growable sequence of characters. It copies and moves like a value, tracks its size, and manages its own storage—making it the normal choice for owned text in C++.

strings.cpp
#include <iostream>
#include <string>

int main() {
    std::string first{"Ada"};
    std::string last{"Lovelace"};
    std::string full{first + " " + last};

    std::cout << full << '\n';
    std::cout << "characters: " << full.size() << '\n';
    std::cout << "first: " << full.front() << '\n';

    if (full.starts_with("Ada")) {
        std::cout << "Expected prefix\n";
    }
}

Watch out

Indexing with text[index] does not check bounds. Use text.at(index)when a checked access is valuable, or prove index < text.size() first.

Token input

Extraction with >> skips leading whitespace and reads one formatted token. For strings, it stops at whitespace; for numbers, it attempts a numeric conversion.

token_input.cpp
#include <iostream>
#include <string>

int main() {
    std::string name;
    int age{};

    std::cout << "First name and age: ";
    if (std::cin >> name >> age) {
        std::cout << name << " is " << age << '\n';
    } else {
        std::cerr << "Expected a word followed by an integer\n";
        return 1;
    }
}

Note

Token extraction fits separated values such as Ada 28. It does not fit a full name such as Ada Lovelace, because the first extraction stops at the space.

Complete-line input

std::getline reads through the delimiter—newline by default—and stores the text without that delimiter. When switching from >>, consume leading whitespace deliberately with std::ws.

line_input.cpp
#include <iostream>
#include <string>

int main() {
    int quantity{};
    std::string description;

    std::cout << "Quantity: ";
    if (!(std::cin >> quantity)) return 1;

    std::cout << "Description: ";
    std::getline(std::cin >> std::ws, description);

    std::cout << quantity << " × " << description << '\n';
}

Watch out

A plain getline(std::cin, description) immediately after numeric extraction may consume the leftover newline and return an empty string. std::getline(std::cin >> std::ws, description) solves that case, but it also discards intentional leading whitespace.

Stream state & validation

A failed formatted extraction sets a failure state and leaves the offending characters in the stream. Recovery requires both clearing the state and discarding the invalid line before trying again.

validated_age.cpp
#include <iostream>
#include <limits>

int read_age() {
    while (true) {
        std::cout << "Age (0-130): ";
        int age{};

        if (std::cin >> age && age >= 0 && age <= 130) {
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            return age;
        }

        std::cout << "Please enter a whole number from 0 to 130.\n";
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
}

int main() {
    std::cout << "Accepted: " << read_age() << '\n';
}
  1. Attempt the extraction
  2. Validate domain rules, not only syntax
  3. On failure, call clear() to reset stream state
  4. Call ignore(..., '\n') to discard the bad line
  5. Explain the expected input and retry

Parsing text

Reading a complete line first separates input acquisition from interpretation. A string stream can then parse the in-memory text without corrupting the state of std::cin.

parse_point.cpp
#include <iostream>
#include <sstream>
#include <string>

bool parse_point(const std::string& text, int& x, int& y) {
    std::istringstream input{text};
    char comma{};

    if (!(input >> x >> comma >> y) || comma != ',') return false;
    input >> std::ws;
    return input.eof(); // reject trailing non-whitespace text
}

int main() {
    int x{};
    int y{};
    if (parse_point("12, -4", x, y)) {
        std::cout << x << ' ' << y << '\n';
    }
}

Key idea

Successful extraction alone may accept a valid prefix such as 12,4junk. After parsing the expected fields, consume whitespace and verify end-of-input when the whole line must match the grammar.

Type-safe formatting

C++20's std::format separates a format template from its typed arguments. It supports alignment, width, precision, numeric bases, and other presentation rules without permanently changing stream state.

modern_format.cpp
#include <format>
#include <iostream>

int main() {
    std::string language{"C++"};
    int modules{30};
    double progress{0.2};

    std::cout << std::format(
        "{} course: {} modules, {:.0%} drafted\n",
        language, modules, progress);
}

Note

Language mode and standard-library implementation are separate. Some older toolchains accept C++20 syntax but do not yet provide <format>. Use stream formatting when your supported library lacks it, and document the minimum toolchain for projects that require it.

Recap & quick check

Key takeaways

  • cin reads standard input; cout carries results; cerr and clog carry diagnostics.
  • std::string owns text, tracks its size, and behaves like a value.
  • Formatted extraction reads tokens; getline reads complete lines.
  • Recovering failed input requires clearing stream state and discarding invalid characters.
  • Parse the complete expected grammar rather than accepting a valid prefix.

Quick check

1. Which stream should carry an error diagnostic?

2. Why can getline return an empty line after operator>>?

3. What two actions recover from invalid formatted input?

4. What does std::string::size report?

Your programs can now communicate reliably. Next: Module 4 — Decisions & Control Flow, where values begin directing program behavior.