How to test whether stringstream operator>> has parsed a bad type and skip it

前端 未结 5 2097
误落风尘
误落风尘 2020-11-21 06:49

I am interested in discussing methods for using stringstream to parse a line with multiple types. I would begin by looking at the following line:



        
相关标签:
5条回答
  • 2020-11-21 07:22

    If you like concision - here's another option that (ab?)uses && to get cout done only when a number's been parsed successfully, and when a number isn't parsed it uses the comma operator to be able to clear() stream error state inside the conditional before reading a character to be ignored...

    #include <iostream>
    #include <sstream>
    #include <string>
    
    int main()
    {
        std::istringstream iss("2.832 1.3067 nana1.678 x-1E2 xxx.05 meh.ugh");
        double num = 0;
        char ignored;
        while (iss >> num && std::cout << num << '\n' ||
               (iss.clear(), iss) >> ignored)
            ;
    }
    

    http://ideone.com/WvtvfU

    0 讨论(0)
  • 2020-11-21 07:27

    I have built up a more fine tuned version for this, that is able to skip invalid input character wise (without need to separate double numbers with whitespace characters):

    #include <iostream>
    #include <sstream>
    #include <string>
    using namespace std;
    
    int main() {
    
        istringstream iss("2.832 1.3067 nana1.678 xxx.05 meh.ugh");
        double num = 0;
        while(iss >> num || !iss.eof()) {
            if(iss.fail()) {
                iss.clear();
                while(iss) {
                    char dummy = iss.peek();
                    if(std::isdigit(dummy) || dummy == '.') {
                        // Stop consuming invalid double characters
                        break;
                    }
                    else {
                        iss >> dummy; // Consume invalid double characters
                    }
                }
                continue;
            }
            cout << num << endl;
        }
        return 0;
    }
    

    Output

     2.832
     1.3067
     1.678
     0.05
    

    Live Demo

    0 讨论(0)
  • 2020-11-21 07:27

    You can use std::istringstream::eof() to validate input like this:

    #include <string>
    #include <sstream>
    #include <iostream>
    
    // remove white-space from each end of a std::string
    inline std::string& trim(std::string& s, const char* t = " \t")
    {
        s.erase(s.find_last_not_of(t) + 1);
        s.erase(0, s.find_first_not_of(t));
        return s;
    }
    
    // serial input
    std::istringstream in1(R"~(
     2.34 3 3.f 3.d .75 0 wibble 
    )~");
    
    // line input
    std::istringstream in2(R"~(
    2.34
     3
    
    3.f
    3.d
    .75
    0
    wibble 
    )~");
    
    int main()
    {
        std::string input;
    
        // NOTE: This technique will not work if input is empty
        // or contains only white-space characters. Therefore
        // it is safe to use after a conditional extraction
        // operation >> but it is not reliable after std::getline()
        // without further checks.
    
        while(in1 >> input)
        {
            // input will not be empty and will not contain white-space.
            double d;
            if((std::istringstream(input) >> d >> std::ws).eof())
            {
                // d is a valid double
                std::cout << "d1: " << d << '\n';
            }
        }
    
        std::cout << '\n';
    
        while(std::getline(in2, input))
        {
            // eliminate blank lines and lines
            // containing only white-space (trim())
            if(trim(input).empty())
                continue;
    
            // NOW this is safe to use
    
            double d;
            if((std::istringstream(input) >> d >> std::ws).eof())
            {
                // d is a valid double
                std::cout << "d2: " << d << '\n';
            }
        }
    }
    

    This works because the eof() check ensures that only the double was entered and not garbage like 12d4.

    0 讨论(0)
  • 2020-11-21 07:40

    Few minor differences to πάντα ῥεῖ's answer - makes it also handle e.g. negative number representations etc., as well as being - IMHO - a little simpler to read.

    #include <iostream>
    #include <sstream>
    #include <string>
    
    int main()
    {
        std::istringstream iss("2.832 1.3067 nana1.678 x-1E2 xxx.05 meh.ugh");
        double num = 0;
        for (; iss; )
            if (iss >> num)
                std::cout << num << '\n';
            else if (!iss.eof())
            {
                iss.clear();
                iss.ignore(1);
            }
    }
    

    Output:

    2.832
    1.3067
    1.678
    -100
    0.05
    

    (see it running here)

    0 讨论(0)
  • 2020-11-21 07:41

    The following code works well to skip the bad word and collect the valid double values

    istringstream iss("2.832 1.3067 nana 1.678");
    double num = 0;
    while(iss >> num || !iss.eof()) {
        if(iss.fail()) {
            iss.clear();
            string dummy;
            iss >> dummy;
            continue;
        }
        cout << num << endl;
    }
    

    Here's a fully working sample.


    Your sample almost got it right, it was just missing to consume the invalid input field from the stream after detecting it's wrong format

     if (parser.fail()) {
         std::cout << "Failed!" << std::endl;
         parser.clear();
         string dummy;
         parser >> dummy;
     }
    

    In your case the extraction will try to read again from "nana" for the last iteration, hence the last two lines in the output.

    Also note the trickery about iostream::fail() and how to actually test for iostream::eof() in my 1st sample. There's a well known Q&A, why simple testing for EOF as a loop condition is considered wrong. And it answers well, how to break the input loop when unexpected/invalid values were encountered. But just how to skip/ignore invalid input fields isn't explained there (and wasn't asked for).

    0 讨论(0)
提交回复
热议问题