String Parsing

Hello all, I have a text file that has multiple geometries in it that I'm trying to parse. The issues is that they are delimited differently. I am looking for an elegant way to parse the following data set.

Data Set:
1
2
3
4
5
6
7
8
9
10
11
vt 0.350000 0.750000
vt 0.400000 0.750000
vt 0.450000 0.750000
...
vn 0.904107 -0.309059 0.295082
vn 0.951042 -0.309059 0.001256
vn 0.799426 -0.156457 -0.580033
...
f 99/103/99 100/104/100 119/124/119
f 101/126/102 120/125/120 81/105/82
f 100/104/100 81/105/82 120/125/120


This is what I have so far, which reads the geometry that is delimited by white space perfectly. However, I can't figure out an elegant solution to parsing the "f" data. I thought of exploiting getline() to include a delimiter "/", but that isn't working properly since the mixed configuration in the file.

The following code works well for the numbers that are white-space separated, but after multiple attempts, I haven't found an elegant solution to parsing the "f" data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void loadInput()
{
    int count = 0;
    string s;
    char buffer[MAX_BUFFER_SIZE]; //128
    
    while(cin.getline(buffer, MAX_BUFFER_SIZE)){
        stringstream ss(buffer);
        ss >> s;
        if(s=="v"){
                ss >> V[0] >> V[1] >> V[2];
                cout << "V = " << V[0] << "," << V[1] << "," << V[2] <<"\n";
                vecv.push_back(V);
        }
        else if(s=="vn"){
                ss >> V[0] >> V[1] >> V[2];
                cout << "VN = " << V[0] << "," << V[1] << "," << V[2] <<"\n";
                vecn.push_back(V);
        }
        else if (s == "f"){
                
                ?????

        }
    }
}


Any advice? I'm still fuzzy on I/O so hoping to learn coming out of this.
Your code will be similar to what you have for the first two patterns. The data for the "f" lines appear to have three columns of three numbers each delimited by a forward slash character.

What is exactly the problem you are trying to solve? Are you also trying to process the three columns, or are you first trying to store them into a vector? Did I miss where you have the array V defined?
I've included some of the global variable declarations. This is a small snippet of the code, but in context.. I'm trying to learn openGL (and C++ simultaneously) the goal is to read in object files (in this case, a list of vertices, tangents, normals, and faces) and store it in vectors vecv (vertices), vecn (normals) vecf(faces).

Note, Vector3f is a vector class that is more of a mathematical description of a vector (contains 3 coords, xyz... dot products, cross products etc...)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Vector3f V;
// This is the list of points (3D vectors)
vector<Vector3f> vecv;

// This is the list of normals (also 3D vectors)
vector<Vector3f> vecn;

// This is the list of faces (indices into vecv and vecn)
vector<vector<unsigned> > vecf;
void loadInput()
{
    int count = 0;
    string s;
    char buffer[MAX_BUFFER_SIZE]; //128
    
    while(cin.getline(buffer, MAX_BUFFER_SIZE)){
        stringstream ss(buffer);
        ss >> s;
        if(s=="v"){
                ss >> V[0] >> V[1] >> V[2];
                cout << "V = " << V[0] << "," << V[1] << "," << V[2] <<"\n";
                vecv.push_back(V);
        }
        else if(s=="vn"){
                ss >> V[0] >> V[1] >> V[2];
                cout << "VN = " << V[0] << "," << V[1] << "," << V[2] <<"\n";
                vecn.push_back(V);
        }
        else if (s == "f"){

             ss >> V[0] >> V[1] >> V[2];
            cout << V[0] << V[1] << V[2] << " | ";
            //vecf[0].push_back(V);
            ss >> V[0] >> V[1] >> V[2];
            cout << V[0] << V[1] << V[2] << " | ";
            //vecf[1].push_back(V);
            cout << V[0] << V[1] << V[2];
            ss >> V[0] >> V[1] >> V[2];
            //vecf[2].push_back(V);
            cout << V[0] << V[1] << V[2] << "\n";
 
        }
    }
}


The output for the faces is kind of capturing the first 3 numbers in the set of 9. Last two lines of input:

1
2
f 379/397/379 380/398/380 382/401/382
f 380/398/380 361/399/361 382/401/382


Last two lines of output using above code:
1
2
 379 0 -0 | 379 0 -0 | 379 0 -0
 380 0 -0 | 380 0 -0 | 380 0 -0


I expected it to say (or rather, I WANT it to say)
1
2
379 397 379  |  380 398 380 |  382 401 382
380 398 380  |  361 399 361 |  382 401 382
Last edited on
Thanks, that is a little more helpful. Without knowing how the ">>" operator is overloaded in Vector3f, it's hard to know what is happening. Perhaps the documentation could provide a clue?
I've included some of the global variable declarations.

Why the global variables? You should learn to pass the variables to and from functions as required.

1
2
3
   char buffer[MAX_BUFFER_SIZE]; //128
    
    while(cin.getline(buffer, MAX_BUFFER_SIZE)){

Why are you using the C-string? You already know about std::string so I suggest you use it here as well:
1
2
3
   string buffer;
    
    while(getline(cin, buffer)){


When parsing for the "f" block don't forget to remove that delimiter (the '/' character). You can do this be retrieving a char and throwing it away:

1
2
             char delimiter;
             ss >> V[0] >> delimiter >> V[1] >> delimiter >> V[2];

Last edited on
Thanks for your help guys. For any that run into this issue after me, this is how I solved it using the above suggetions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void loadInput()
{
    // load the OBJ file here
    char delimiter;
    //counter variable to handle delimiter locations (2 delimiters per set of 3 numbers)
    unsigned z=0;
    //temporary storage for appends
    unsigned temp;
    vector<unsigned> V1;
    Vector3f V;
    //string storage for getline and parsing
    string buffer;
    string s;

    while(getline(cin, buffer)){
        stringstream ss(buffer); // create ss object
        ss >> s; // store first entry (always a character identifier (v= vertex, vn = normal, f = face)
        if(s=="v"){
                ss >> V[0] >> V[1] >> V[2];
                cout << "V = " << V[0] << "," << V[1] << "," << V[2] <<"\n";
                vecv.push_back(V);
        }
        else if(s=="vn"){
                ss >> V[0] >> V[1] >> V[2];
                cout << "VN = " << V[0] << "," << V[1] << "," << V[2] <<"\n";
                vecn.push_back(V);
        }
        else if (s == "f"){
                while(ss >> temp){ // For an arbitrary number of faces (goes until end of line)
                V1.push_back(temp); // append all face data to vector
                    if(z!=2){ // z cycles between [0,1,2], on 3rd cycle there is no delimiter. Data format : a/b/c
                        ss >> delimiter; //only parse delimiter for first two cycles
                    }
                z = (z+1)%3; // increment z
                }
                vecf.push_back(V1); // 2-D vector of face data
        }
    }
}


And to answer your questions
1) I inherited "skeleton code" from an online course I'm following along with (MIT OCW) to learn openGL, and that's the way it was structured.
2) Great point, the assignment gave a suggestion, which I followed using C-strings. I've since switched to your suggestion

Also for those interested in the problem I'm working on see:
http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-837-computer-graphics-fall-2012/assignments/
I'm' working on Assignment0
Topic archived. No new replies allowed.