Forcing String to int Function to Consume Entire String

前端 未结 2 817
青春惊慌失措
青春惊慌失措 2020-12-04 01:59

Given a string that should represent a number, I\'d like to put it into a conversion function which would provide notification if the whole string did not convert.<

相关标签:
2条回答
  • 2020-12-04 02:43

    Edit: In c++17 or later from_chars is preferred. See here for more: https://topanswers.xyz/cplusplus?q=724#a839


    For a given string str there are several ways to accomplish this each with advantages and disadvantages. I've written a live example here: https://ideone.com/LO2Qnq and discuss each below:

    strtol

    As suggested here strtol's out-parameter can be used to get the number of characters read. strtol actually returns a long not an int so a cast is happening on the return.

    char* size;
    const int num = strtol(str.c_str(), &size, 10);
    
    if(distance(str.c_str(), const_cast<const char*>(size)) == str.size()) {
        cout << "strtol: " << num << endl;
    } else {
        cout << "strtol: error\n";
    }
    

    Note that this uses str.c_str() to refer to the same string. c_str Returns pointer to the underlying array serving as character storage not a temporary if you have C++11:

    c_str() and data() perform the same function

    Also note that the pointer returned by c_str will be valid between the strtol and distance calls unless:

    • Passing a non-const reference to the string to any standard library function
    • Calling non-const member functions on the string, excluding operator[], at(), front(), back(), begin(), rbegin(), end() and rend()

    If you violate either of these cases you'll need to make a temporary copy of i's underlying const char* and perform the test on that.

    sscanf

    sscanf can use %zn to return the number of characters read which may be more intuitive than doing a pointer comparison. If base is important, sscanf may not be a good choice. Unlike strtol and stoi which support bases 2 - 36, sscanf provides specifiers for only octal (%o), decimal (%d), and hexadecimal (%x).

    size_t size;
    int num;
    
    if(sscanf(str.c_str(), "%d%zn", &num, &size) == 1 && size == str.size()) {
        cout << "sscanf: " << num << endl;
    } else {
        cout << "sscanf: error\n";
    }
    

    stoi

    As suggested here stoi's output parameter works like sscanf's %n returning the number of characters read. In keeping with C++ this takes a string and unlike the C implementations above stoi throws an invalid_argument if the first non-whitespace character is not considered a digit for the current base, and this unfortunately means that unlike the C implementations this must check for an error in both the try and catch blocks.

    try {
        size_t size;
        const auto num = stoi(str, &size);
    
        if(size == str.size()) {
            cout << "stoi: " << num << endl;
        } else {
            throw invalid_argument("invalid stoi argument");
        }
    } catch(const invalid_argument& /*e*/) {
        cout << "stoi: error\n";
    }
    
    0 讨论(0)
  • 2020-12-04 02:58

    Alternatively you can use std::istringstream as you mentioned, but check to make sure it parsed to the end of the stream. Assuming you have a constant reference, you could do something like the following

    T parse(const std::string& input) {
        std::istringstream iss(input);
            T result;
            iss >> result;
            if (iss.eof() || iss.tellg() == int(input.size())) {
                return result;
            } else {
                throw std::invalid_argument("Couldn't parse entire string");
        }
    }
    

    The benefit of this approach is that you parse anything that overloads operator>>. Note: I'm not entirely sure if the condition is enough, but with my testing it seemed to be. For some reason the stream would get a failure marking if it parsed to the end.

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