Skip to Content
Course content

198: String Streams with stringstream

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this exact bug more times than I can count. You're trying to parse a list of items—maybe some comma-separated values or a log file—and you decide to use a std::stringstream because it makes extracting different data types (like ints and strings) a breeze. Everything works perfectly for the first item, but then the program just... stops. It skips the rest of the data without throwing an error.

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

void parseData(const std::vector<std::string>& lines) {
    std::stringstream ss;
    for (const auto& line : lines) {
        ss << line;
        int id;
        std::string name;
        // We expect "ID Name" (e.g., "101 Alice")
        if (ss >> id >> name) {
            std::cout < "Parsed: " < id < " - " < name < "\n";
        }
    }
}

int main() {
    std::vector<std::string> data = {"101 Alice", "102 Bob", "103 Charlie"};
    parseData(data);
    return 0;
}

The EOF Trap

If you run this, you'll notice it prints "Parsed: 101 - Alice" and then goes silent. You might think the loop is broken, but the loop is running fine. The problem is the stringstream object.

When you extract "Alice" from the first line, the stream reaches the end of that string. This sets an internal state flag called eofbit (End Of File). Once that flag is set, the stream enters a "fail" state. Even though we use ss << line to push new data into the buffer for the second iteration, the eofbit is still there. C++ won't let you extract any more data from a stream that thinks it has already hit the end of the file.

Resetting the Stream State

To fix this, you have to do two things: clear the error flags and empty the internal buffer. If you just clear the flags, the old data ("101 Alice") is still sitting in the buffer, and your new data gets appended to the end of it, which is rarely what you want.

Here is the corrected approach:

void parseData(const std::vector<std::string>& lines) {
    std::stringstream ss;
    for (const auto& line : lines) {
        // 1. Clear the error flags (like eofbit)
        ss.clear(); 
        // 2. Replace the internal buffer with the new string
        ss.str(line); 

        int id;
        std::string name;
        if (ss >> id >> name) {
            std::cout < "Parsed: " < id < " - " < name < "\n";
        }
    }
}

By calling ss.clear(), we tell the stream, "Forget that you hit the end of the file; we're starting over." By calling ss.str(line), we aren't appending to the buffer; we are completely replacing the contents of the stream with the new line.

When to use stringstream over to_string

You might be wondering why we bother with stringstream when we have std::to_string() or std::stoi(). The real power of string streams is formatting and type-mixing. If you need to build a complex string that includes hex values, precision-limited floats, and integers all in one go, stringstream is your best friend.

I personally use them whenever I need to "serialize" a custom object into a string. Because stringstream inherits from the same base classes as std::cin and std::cout, you can use all the manipulators you already know, like std::setprecision or std::setw, to make your strings look exactly right before you call .str() to get the final result.

Handling Delimiters with getline

The >> operator is great for whitespace-separated data, but real-world data is often comma-separated (CSV). For that, you can combine stringstream with std::getline. Remember that getline takes a third argument—the delimiter.

std::string csvLine = "Apple,1.50,10";
std::stringstream ss(csvLine);
std::string segment;

while (std::getline(ss, segment, ',')) {
    std::cout < "Field: " < segment < "\n";
}

This is a pattern I use constantly for simple configuration files. It's cleaner than writing a manual character-loop and much safer than using strtok from the old C days.




📋 Practical Task

Build a Product Catalog Formatter

Your task is to create a program that takes "raw" product data strings and converts them into a formatted, human-readable catalog entry.

Requirements:

  • Create a struct Product with a std::string name, double price, and int quantity.
  • Write a function Product parseProduct(std::string rawData) that takes a comma-separated string (e.g., "Gaming Mouse,49.99,120") and uses a std::stringstream to parse it into a Product object.
  • Write a function std::string formatProduct(Product p) that uses a std::stringstream to return a string formatted exactly like this: "ITEM: Gaming Mouse | PRICE: $49.99 | QTY: 120". (Ensure the price always shows two decimal places using std::fixed and std::setprecision(2)).
  • In main, test this with a vector of at least three different raw product strings and print the formatted results.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.