3

I am using a istringstream to read a string word by word. However, when my condition fails I need to be able to revert the istringstream to before the previous word was read. My example code works, but I want to know if there is a more direct way to use streams to accomplish this.

std::string str("my string");
std::istringstream iss(str);

std::ostringstream ossBackup << iss.rdbuf(); // Writes contents of buffer and in the process changes the buffer
std::string strBackup(ossBackup.str());      // Buffer has been saved as string

iss.str(strBackup);                          // Use string to restore iss's buffer
iss.clear();                                 // Clear error states
iss >> word;                                 // Now that I have a backup read the 1st word ("my" was read)

// Revert the `istringstream` to before the previous word was read.
iss.str(strBackup);                         // Restore iss to before last word was read
iss.clear();                                // Clear error states
iss >> word;                                // "my" was read again
user870130
  • 565
  • 1
  • 8
  • 16
  • 1
    Well, that seems to depend on how you define _"when my condition fails"_ exactly. You could well just inspect values that are left by a `operator>>()` operation, and had set the stream state to fail. Here's an [example](http://stackoverflow.com/questions/24504582/test-whether-stringstream-operator-has-parsed-a-bad-type?noredirect=1#comment37965807_24504582) for such approach. – πάντα ῥεῖ Oct 06 '14 at 18:28

2 Answers2

3

You can use tellg() and seekg() to save and restore your position if you like:

#include <string>
#include <sstream>

int main()
{
    std::istringstream iss("some text");

    std::string word;

    // save the position
    std::streampos pos = iss.tellg();

    // read a word
    if(iss >> word)
        std::cout << word << '\n';

    iss.clear(); // clear eof or other errors
    iss.seekg(pos); // move to saved position

    while(iss >> word)
        std::cout << word << '\n';

}
Galik
  • 47,303
  • 4
  • 80
  • 117
  • Well it seems so obvious to me now that you've said it that I feel silly for being unable to think of an efficient solution. – Mooing Duck Oct 06 '14 at 19:09
1

This is really only guaranteed to work for stringstream's, but you can repeatedly call unget() until you've reached a space character:

#include <iostream>
#include <sstream>

template <int n>
std::istream& back(std::istream& is)
{
    bool state = is.good();

    auto& f = std::use_facet<std::ctype<char>>(is.getloc());
    for (int i = 0; i < n && is; ++i)
        while (is.unget() && !f.is(f.space, is.peek()));

    if (state && !is)
        is.clear();
    return is;
}

int main()
{
    std::stringstream iss("hello world");
    std::string str;

    std::cout << "Value Before: ";
    iss >> str;

    std::cout << str << std::endl;
    iss >> back<1>; // go back one word

    std::cout << "Value after: ";
    iss >> str;

    std::cout << str;
}

Live Demo

David G
  • 94,763
  • 41
  • 167
  • 253