Search code examples
c++stringstreamc++98

Converting string to floats; can't clear stringstream correctly?


Disclaimer: I must use c++ 98

As part of a class assignment, I have been tasked to convert space-delimited strings into floats (then calculate a span with those floats, but that is irrelevant to my problem). The strings are coming from text files. If there are any discrepancies with the float, I am supposed to ignore it and consider it corrupted. For example, a text file could consist of a line that looks like this:

34.6 24.2 18.a 54.3 20.0 15.6

In this case, 18.a would simply be considered corrupt and no further manipulation has to be done to it.

Now, I am having a problem clearing my stringstream of corrupt data. For reference, here is my code:

#include <vector>
#include <limits>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
using namespace std;

int main(int argc, char* argv[]) {

    //Open file
    ifstream infile("dataFile");

    //Get all file input into a single string
    string line;
    string buffer;
    while (getline(infile, buffer)) {
        line += buffer + " ";
    }

    infile.close();

    //Populate vector
    float temp;

    //I have tried to clear the stream with `data >> dummy` for
    //both string and single char types below, but `data >> string`
    //always clears too much, and `data >> char` doesn't seem to clear
    //correctly either

    //string dummy;
    //char dummy;
    vector<float> temps;
    istringstream data(line);
    while (data) {
        //values between -100 and 100 are also considered corrupt
        if (data >> temp && (temp <= 100 && temp >= -100)) {
            temps.push_back(temp);
        }
        else if (!data.eof()) {
            data.clear();

            //trying to ignore all characters until I reach a space
            //but that doesn't work correctly either

            data.ignore(numeric_limits<streamsize>::max(), ' ');
            //data >> dummy;
            //cout << "Dummy: " << dummy << endl;
            temps.push_back(-101.0);
        }
    }

    //display resulting vector values
    for(int i=0; i<temps.size(); ++i) {
        cout << temps[i] << " ";
    }
    cout << endl;
}

My issue lies within the while (data) loop, specifically, inside the else if (!data.eof()) block. When data >> temp (type float) fails, the else if block runs. I clear the consequential failbit and attempt to ignore the remaining characters until the next space-delimiter comes up. However, a text file with a line like such:

a *a -100.1 100.1 a 10.a a 13-6s 12abc -12.a

produces problems. 13 and -6 are both processed as valid floats. I want to ignore the entire chunk of 13-6s, because these values are intended to be space-delimited.

What is the correct way to deal with this istringstream issue, where the characters are not being ignored the way I want?

I have been told by my professor that I can accomplish this with very basic STL techniques. He explicitly recommended to use stringstream as a way to parse floats. Is he in the wrong here?

Please comment for further clarity, if needed; I've been at this for quite some time now and would much appreciate some help.

Thank you!


Solution

  • This should do what you need.

    #include <iostream>
    #include <string>
    #include <sstream>
    
    int main() {
      std::string temp;
    
      // cin will write to temp with each space delimited entry
      while (std::cin >> temp) {
        std::stringstream s(temp);
        float f;
    
        // the first case checks if the actual write the float succeeds
        // the second case checks if the entire stringstream has been read
        if (!(s >> f) || !s.eof()) {
          std::cout << temp << " failed!" << std::endl;
        }
        else {
          std::cout << f << std::endl;
        }
      }
    }
    

    Apologies for not being able to answer your stringstream question but this solution should remove any necessity for that.

    Note that input of 34.6 24.2 18.a 54.3 20.0 15.6 returns an output of:

    34.6
    24.2
    18.a failed!
    54.3
    20
    15.6
    

    Edit: I added a case to the if statement to handle the stranger cases (i.e. 13-6s). It's a neat solution I found here.

    Edit 2: I annotated some of the more complicated parts.