Thread: Can come one explain this code to me about parsing a string?

  1. #1
    Registered User
    Join Date
    Oct 2016
    Posts
    4

    Post Can come one explain this code to me about parsing a string?

    I found this code that takes a a long string of words separated by spaces. it separates the string in to individual words and stores them in to a vector. then it calls the individual words and does something and then it returns with only the numbers in the words.
    I am wondering exactly it separates the numbers from the words.

    so in simpler terms it takes this string ""12as 32312dsda sda23asd" and separates it to 3 strings: "12as" "32312dsda" "sda23asd" and puts it in a vector. after that i don't understand what is going after that, on can someone explain it?

    Code:
    #include <iostream>
    #include <string>
    #include <vector>
    #include <sstream>
    using namespace std;
    
    int main()
    {
        int i;
        int testNumber;
        char testChar;
        string input;
        stringstream ss;
        vector<string> myStrings;
        vector<int> myIntegers;
    
        cout << "Enter the string : "; getline(cin, input);
    
        ss << input;
        while(ss >> input) myStrings.push_back(input);
    
        for(i = 0; i < myStrings.size(); i++)
        {
            ss.str("");
            ss.clear();
            ss << myStrings[i];
    
            bool bQuit = false;
            while(bQuit == false)
            {
                if(ss >> testNumber) 
                {
                    myIntegers.push_back(testNumber);
                    bQuit = true;
                }
                else
                {
                    ss.clear();
                    if(!(ss >> testChar)) bQuit = true;
                }
            }
        }
        
    
        cout << "The numbers in the strings : " << endl;
        for(i = 0; i < myIntegers.size(); i++)
        {
            cout << myIntegers[i] << endl;
        }
    
        cin.get();
        return 0;
    }

  2. #2
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    After the string is split into three and placed in a vector, you enter a loop that will access all of myString's elements. These elements are placed into a stringstream one at a time.

    Once you get to here in the code, there are a couple of possibilities.
    Code:
                if(ss >> testNumber)
    It is likely that the attempt will succeed in some way. For example, with "12as" and "32312dsda" you will store 12 and 32312 into testNumber respectively. Then testNumber is put into myIntegers, and the program continues to the next string.

    In the case that the above attempt does not work, like in the third string "sda23asd", it is handled with this block:
    Code:
               else
               {
                    ss.clear();
                    if(!(ss >> testChar)) bQuit = true;
                }
    In this block, you make the stringstream forget the previous failure by calling ss.clear(); The if statement will extract one character from the string, and since that was successful, you ignore the statement next to if and go back to the top of the while. Then you attempt to extract a number again. You will likely have to extract a few characters before the attempt to grab a number succeeds. After all, "sda23asd" starts with three non-numeric characters in it. And there is a chance that no number is actually in the string at all. If that is the case then
    Code:
    if (!(ss >> testChar)) bQuit = true;
    when you reach the end of the string this will be true, bQuit is made true, and the loop while breaks.

    It is worth noting that there are some strings for which this strategy does not work, like "1xyz2". There are two integers you could parse out of this string. With that in mind, I think there is a more robust solution:
    Code:
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <vector>
    using namespace std;
    
    int main() {
        string line;
        cout << "Enter the string: ";
        getline(cin, line);
    
        stringstream ss(line);
        vector<int> myIntegers;
        while (!ss.eof())
        {
            int testInt;
            if (ss >> testInt)
            {
                myIntegers.push_back(testInt);
            }
            else
            {
                ss.clear();
                ss.ignore(); // extract and discard the non-numeric character
            }
        }
    
        cout << "The numbers in the strings: " << endl;
        for (unsigned int i = 0; i < myIntegers.size(); i++)
        {
            cout << myIntegers[i] << endl;
        }
        return 0;
    }
    
    // my output below:
    Enter the string: 12as 32312dsda sda23asd 1xyz2
    The numbers in the strings:
    12
    32312
    23
    1
    2
    Notice however that I did take out one step in your algorithm. While it isn't wrong to separate the strings by spaces, if the integers are really what you want then putting the whole thing into a stringstream and parsing until eof() is true - meaning, there is no more string - is enough.

    HTH
    Last edited by whiteflags; 10-26-2016 at 10:01 PM.

  3. #3
    Registered User
    Join Date
    Oct 2016
    Posts
    4
    Thank you so much, you explanation was on point and easy to understand.
    Last edited by nonekiller001; 10-26-2016 at 10:38 PM.

  4. #4
    Registered User
    Join Date
    Oct 2016
    Posts
    4
    It is worth noting that there are some strings for which this strategy does not work, like "1xyz2". There are two integers you could parse out of this string. With that in mind, I think there is a more robust solution:
    Code:
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <vector>
    using namespace std;
    
    int main() {
        string line;
        cout << "Enter the string: ";
        getline(cin, line);
    
        stringstream ss(line);
        vector<int> myIntegers;
        while (!ss.eof())
        {
            int testInt;
            if (ss >> testInt)
            {
                myIntegers.push_back(testInt);
            }
            else
            {
                ss.clear();
                ss.ignore(); // extract and discard the non-numeric character
            }
        }
    
        cout << "The numbers in the strings: " << endl;
        for (unsigned int i = 0; i < myIntegers.size(); i++)
        {
            cout << myIntegers[i] << endl;
        }
        return 0;
    }
    
    // my output below:
    Enter the string: 12as 32312dsda sda23asd 1xyz2
    The numbers in the strings:
    12
    32312
    23
    1
    2
    Notice however that I did take out one step in your algorithm. While it isn't wrong to separate the strings by spaces, if the integers are really what you want then putting the whole thing into a stringstream and parsing until eof() is true - meaning, there is no more string - is enough.

    HTH
    How would i adapt my to use your robust algorithm?
    i am reading a text file line by line so i cant use while(!ss.eof())

    the text file would look like this:
    Mickey Mouse S45 78Y W91
    Minnie Mouse HH95DF E92DD P88D
    Jack Robinson
    Jimmy Johnson SSF54S W89W U66T DD44S DD21S
    Donald Duck WQ72Q A81B EE89W

    and i would want to read one line at a time so i could calculate the sum of the line.

    Code:
    void getName(ifstream& in, string& firstName, string& lastName)
    {
        char a;
        in >> firstName >> lastName;
        //cout << "sucess" << firstName << lastName << endl;
    
    }
    
    void readData(ifstream& in, double& sum, int& count)
    {
        int i;
        int testNumber;
        double total;
        char testChar;
        string line;
        vector<string> myStrings;  //data container
        vector<int> myIntegers;
        count = 0;
        sum = 0;
    
        getline(in, line); //get data from scores after first lastname as a string, like a sentence.
        stringstream ss(line); // load 1 word from string in to stringstream, making it usuable like a cin (Splits string line in to words)
        while (ss >> line)
        {
            myStrings.push_back(line); //while loading  stream ss in to string line, put string in to a vector, so storeing each word in string line in the vector
        }
    
        for (i = 0; i < myStrings.size(); i++) //  i is lower than vector size
        {
            ss.str(""); //clears contents of ss string
            ss.clear(); // clear any error flasts from ss string
            ss << myStrings[i];
    
            //ss >> line;
            //cout << line << endl;
    
            bool bQuit = false;
            while (bQuit == false)
                //try to put string in a int, if fail clear bad flag and read from nect char in string till all numbers found. stor number in vector.
            {
                if (ss >> testNumber)  //if string starts with numbers take numbers and store in vector
                {                        //does not work if starts with letters
                                        //cout << ss.str() << endl;
                    myIntegers.push_back(testNumber);
    
                    bQuit = true;
                }
                else   //if starts with letters clear previous bad flag and read string  till hit a number, then exit
                {
                    ss.clear(); //clear bad flag
    
                    if (!(ss >> testChar)) bQuit = true; // if string goes in to char then its a letter, exit loop.
                }
            }
        }
    
    
        for (i = 0; i < myIntegers.size(); i++)
        {
            //f    cout << myIntegers[i] << endl;
            if (count == 0)
    
            {
                sum = myIntegers[i];
            }
            else
            {
                sum += myIntegers[i];
            }
            ++count;
            //cout << sum << endl << count;
        }
    
        //cin.get();
    }
    i read the names in the getName() function so the readData() function read the rest of the line.

  5. #5
    Registered User
    Join Date
    Oct 2016
    Posts
    4
    Never mind i figured it out, thanks again!
    i think my problem was that i tried to load the string vector in to the string stream and my logic was wrong, remove the vector and it works great now.

  6. #6
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Yeah, that was basically it. You don't need a vector of strings at all, or any code around that. You can still read the file line by line. All ss.eof() means is that you've completely parsed the given line.

    I know it sounds like you figured it out, and I'm glad. I thought it would be good to get a confirmation from me on it.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Parsing string
    By john7 in forum C Programming
    Replies: 20
    Last Post: 12-14-2012, 06:43 AM
  2. Parsing a string
    By flexo87 in forum C Programming
    Replies: 3
    Last Post: 01-24-2009, 03:03 AM
  3. String parsing(parsing comments out of HTML file)
    By slcjoey in forum C# Programming
    Replies: 0
    Last Post: 07-29-2006, 08:28 PM
  4. Parsing a string
    By borre_mosch in forum C++ Programming
    Replies: 10
    Last Post: 03-04-2005, 06:36 PM
  5. Could someone explain this code, string
    By sjalesho in forum C Programming
    Replies: 7
    Last Post: 11-26-2003, 02:54 PM

Tags for this Thread