Search code examples
c++scanfstringstream

How to translate scanf exact matching into modern c++ stringstream reading


I am currenlty working on a project and I'd like to use modern cpp instead of relying on old c for reading files. For context I'm trying to read wavefront obj files.

I have this old code snippet :

const char *line;
float x, y, z;
if(sscanf(line, "vn %f %f %f", &x, &y, &z) != 3)
    break; // quitting loop because couldn't scan line correctly

which I've translated into :

string line;
string word;
stringstream ss(line);
float x, y, z;
if (!(ss >> word >> x >> y >> z)) // "vn x y z"
    break; // quitting loop because couldn't scan line correctly

The difference is that I use a string to skip the first word but I'd like it to match "vn" the same as sscanf does. Is this possible with stringstream or should I keep relying on sscanf for exact pattern matching ?

Also I'm trying to translate

sscanf(line, " %d/%d/%d", &i1, &i2, &i3);

but I'm having a hard time which again orients me towards not using modern cpp for my file reader.


Solution

  • I've run into this requirement as well, and wrote a little extractor for streams that lets you match literals. The code looks like this:

    #include <iostream>
    #include <cctype>
    
    std::istream& operator>>(std::istream& is, char const* s) {
    
            if (s == nullptr)
                    return;
    
            if (is.flags() & std::ios::skipws) {
                    while (std::isspace(is.peek()))
                            is.ignore(1);
    
                    while (std::isspace((unsigned char)* s))
                            ++s;
            }
    
            while (*s && is.peek() == *s) {
                    is.ignore(1);
                    ++s;
            }
            if (*s)
                    is.setstate(std::ios::failbit);
            return is;
    }
    

    In your case, you'd use it something like this:

    if (!(ss >> "vn" >> x >> y >> z))
        break;
    

    As you can see from the code, it pays attention to the skipws state, so it'll skip leading white space if and only if you have skipws set. So if you need to match a pattern that includes a precise number of leading spaces, you want to turn skipws off, and include those spaces in your pattern.