Input with istream

The iostream library contains extensive functionality beyond what we can cover comprehensively. This lesson focuses on the most practical features of the istream class, which handles all input operations in C++.

The extraction operator fundamentals

You've used the extraction operator (>>) extensively to read various data types. C++ provides built-in extraction operations for fundamental types, and you've learned how to overload the extraction operator for custom classes.

A critical concern when reading strings is preventing buffer overflow. Consider this vulnerable code:

char productCode[10]{};
std::cin >> productCode;

If the user enters "LAPTOP-MODEL-2024-PRO" (20 characters), the buffer overflows, causing undefined behavior and potential security vulnerabilities. Never assume users will respect your buffer limits.

Using manipulators to control input

A manipulator is a special object that modifies stream behavior when used with extraction (>>) or insertion (<<) operators. You've already encountered std::endl, which inserts a newline and flushes the output buffer.

The std::setw manipulator (found in <iomanip>) limits the number of characters extracted from a stream:

#include <iomanip>
#include <iostream>

int main()
{
    char productCode[10]{};
    std::cout << "Enter product code: ";
    std::cin >> std::setw(10) >> productCode;

    std::cout << "Registered: " << productCode << '\n';

    return 0;
}

This code reads at most 9 characters (reserving space for the null terminator), preventing buffer overflow. Any additional characters remain in the input stream for subsequent operations.

Extraction operator and whitespace handling

The extraction operator automatically skips whitespace characters (spaces, tabs, and newlines). Observe this behavior:

#include <iostream>

int main()
{
    char letter{};

    std::cout << "Type a sentence and press Enter:\n";

    while (std::cin >> letter)
    {
        std::cout << letter;
    }

    return 0;
}

Input:

Welcome to C++ programming

Output:

WelcometoC++programming

The extraction operator eliminated all whitespace, producing a continuous character sequence. Many situations require preserving whitespace, which leads us to alternative input methods.

Reading with get() to preserve whitespace

The istream class provides get(), which reads characters without skipping whitespace:

#include <iostream>

int main()
{
    char letter{};

    std::cout << "Type a sentence and press Enter:\n";

    while (std::cin.get(letter))
    {
        std::cout << letter;
    }

    return 0;
}

Input:

Welcome to C++ programming

Output:

Welcome to C++ programming

Perfect! The whitespace is preserved.

The get() function also accepts a buffer and maximum character count:

#include <iostream>

int main()
{
    char emailAddress[50]{};

    std::cout << "Enter your email address: ";
    std::cin.get(emailAddress, 50);

    std::cout << "Confirmation email sent to: " << emailAddress << '\n';

    return 0;
}

Input:

developer@example.com

Output:

Confirmation email sent to: developer@example.com

Important consideration with get()

The get() function reads up to the delimiter (typically newline) but does not extract it from the stream. This creates unexpected behavior:

#include <iostream>

int main()
{
    char firstName[25]{};
    char lastName[25]{};

    std::cout << "Enter first name: ";
    std::cin.get(firstName, 25);

    std::cout << "Enter last name: ";
    std::cin.get(lastName, 25);

    std::cout << "Full name: " << firstName << " " << lastName << '\n';

    return 0;
}

Input:

Jordan

Output:

Full name: Jordan

The second get() call immediately encountered the newline character left by the first call, so it extracted nothing. This is where getline() becomes essential.

Reading complete lines with getline()

The getline() function reads up to the delimiter and then extracts (removes) it from the stream:

#include <iostream>

int main()
{
    char firstName[25]{};
    char lastName[25]{};

    std::cout << "Enter first name: ";
    std::cin.getline(firstName, 25);

    std::cout << "Enter last name: ";
    std::cin.getline(lastName, 25);

    std::cout << "Full name: " << firstName << " " << lastName << '\n';

    return 0;
}

Input:

Jordan
Smith

Output:

Full name: Jordan Smith

Counting extracted characters with gcount()

To determine how many characters the last input operation extracted, use gcount():

#include <iostream>

int main()
{
    char description[200]{};

    std::cout << "Enter product description: ";
    std::cin.getline(description, 200);

    std::cout << "Description: " << description << '\n';
    std::cout << "Character count: " << std::cin.gcount() << " (includes delimiter)\n";

    return 0;
}

Input:

High-performance gaming laptop

Output:

Description: High-performance gaming laptop
Character count: 31 (includes delimiter)

Note that gcount() includes the extracted delimiter in its count.

Special getline() for std::string

A specialized getline() function works specifically with std::string objects. This version exists in the <string> header as a standalone function:

#include <iostream>
#include <string>

int main()
{
    std::string companyName{};

    std::cout << "Enter company name: ";
    std::getline(std::cin, companyName);

    std::cout << "Registered company: " << companyName << '\n';

    return 0;
}

This approach is generally preferable to C-style character arrays because std::string handles memory management automatically and grows as needed.

Additional useful istream functions

Several other istream member functions provide fine-grained control:

ignore() - Discards the next character from the stream ignore(int nCount) - Discards the next nCount characters peek() - Examines the next character without removing it from the stream unget() - Returns the last-read character back to the stream putback(char ch) - Places a specific character back into the stream

Example using peek():

#include <iostream>
#include <string>

int main()
{
    std::cout << "Enter command (press ? for help): ";

    char nextChar{ static_cast<char>(std::cin.peek()) };

    if (nextChar == '?')
    {
        std::cout << "\nAvailable commands:\n";
        std::cout << "  S - Save file\n";
        std::cout << "  L - Load file\n";
        std::cout << "  Q - Quit\n";
    }
    else
    {
        std::string command{};
        std::getline(std::cin, command);
        std::cout << "Executing: " << command << '\n';
    }

    return 0;
}

The istream class contains many more specialized functions and variations. Consult reference sites like cppreference.com for comprehensive documentation when you need advanced functionality.

Summary

Extraction operator fundamentals: The extraction operator (>>) reads various data types, but requires protection against buffer overflows when reading into C-style character arrays.

std::setw manipulator: The std::setw manipulator limits the number of characters extracted from a stream, preventing buffer overflow by specifying the maximum number of characters to read (including space for the null terminator).

Whitespace handling: The extraction operator automatically skips whitespace characters (spaces, tabs, newlines), which can be problematic when whitespace needs to be preserved.

get() method: The get() function reads characters without skipping whitespace, preserving spaces, tabs, and newlines. It can read single characters or fill a buffer, but leaves the delimiter (typically newline) in the stream.

getline() method: The getline() function reads up to the delimiter and then extracts (removes) it from the stream, solving the problem of leftover delimiters that get() leaves behind.

gcount() method: The gcount() function returns the number of characters extracted by the last input operation, including the delimiter. This helps verify how much data was actually read.

String-specific getline(): A specialized std::getline() function exists in the header for std::string objects, handling memory management automatically and growing as needed.

Additional useful functions: The istream class provides ignore() for discarding characters, peek() for examining without extracting, unget() for returning the last character, and putback() for placing a specific character back into the stream.

Understanding istream's various input methods enables reading data correctly while handling whitespace, delimiters, and buffer boundaries appropriately for different input scenarios.