String to float/int/bool


#1

Hi Jules,

 

Would it be possible to add a flag in String::getFloatValue and other string to value conversion a flag to know if the conversion was a success ?

In our own String class at UVI we have this 

float String::toFloat(bool *pOk = nullptr)

and it would be great to have someting similar in Juce

It is backward compatible with the current code and you can add a pointer to a bool if you want to check for potential error.

 

Thanks,

 


#2

Not super-keen on doing that because it involves some arbitrary decisions, e.g. is an empty string valid, is it ok if there are some unparsed characters on the end? etc.

I generally use other functions like CharacterFunctions::readDoubleValue() or when I'm parsing a string, also because it's often a lot more efficient to work with a CharPointer_UTF8 than a String in a lot of the situations where you end up doing this kind of thing.


#3

An empty string is not valid IMHO.

I would say that unparsed character at the end are not taken into account.

CharacterFunctions::readDoubleValue() is very low level but why not indeed.

FWIW Qt offer this functionnality

http://doc.qt.io/qt-5/qstring.html#toFloat

 

my 2 cents

 


#4

checking back CharacterFunctions::readDoubleValue(), I don't see how it actually reports error.

Am I missing something ?

 

Thanks !


#5

It doesn't report an error, but it leaves the CharPointer at the end of the string, so you can see whether it parsed any characters, and whether there's any more to be read.


#6

Do you find normal to have to do all of this for a basic text to value check ?


String tmp = "3.0";
auto cp = tmp.getCharPointer();
double v = juce::CharacterFunctions::readDoubleValue(cp);
if (cp == tmp.getCharPointer() + tmp.getCharPointer().length() - 1)
{
// v is valid
}

#7

There's no such thing as "normal"!

Everywhere I've ever had to parse a number has had a different requirement for error checking. I've no idea what a standard check would look like, or what should be considered a "normal" error.

For example "3," would be an error (probably) if you're just parsing a single number, but if you're parsing the tokens in a coordinate, it's fine. It's also fine in countries where they use a comma as a decimal place. Parsing "1.0" is fine for a double, but is it an error if you want an int? Is "+2" an error, if sometimes you don't want the "+" to be allowed? Is "123e1" an integer followed by garbage, or is it scientific notation? Is a 500-digit number an error? These are just the first handful of edge-cases I could think of, but there'll be many more!


#8

Your argument have nothing to do with what I'm saying. You argumenting about how to implement a 
readDoubleValue but this is not the point here.

If you really beleive what you are saying, don't provide a readDoubleValue function either but if you do, make it at least a bit more handy to use.

 

 


#9

Sorry, but I thought I made a pretty good case here!

The current implementation doesn't have any internal idea of whether the parse was "OK". So to add this "OK" flag that you want, I'd need to add logic to decide whether this string was indeed "OK" and I honestly don't know what that test would look like!

Anything I was to add would certainly be wrong/incomplete/controversial for some people/circumstances and would start an endless stream of requests: "can you make this method reject/accept [some new edge case]"... And it'd also slow down the implementation of a function that is needed in many performance-sensitive places.

The request you should be making is for a function e.g. isValidNumber (StringRef, ValidNumberOptions). That'd be handy, but not something we have time to do right now..


#10

I am not a Qt advocate, but just saying...

http://doc.qt.io/qt-5/qstring.html#toFloat


#11

Sure, but I think that's a really bad design! They could at least give some documentation about what "valid" means.


#12

I would say the exact same thing valid in the case of standard C aka strtod.

 


#13

Why not just use a classic way of doing this?

template<typename From, typename To> 
    inline bool lexicalConversion(const From & f, To & t)
    {
        std::stringstream ss;
        if ((ss << f) && (ss >> t))
            return true;
        return false;
    }

Now you can do:

std::string str = "123.4f";
double val = 0;
if(lexicalConversion(str, val))
{
    // successful conversion into val (in terms of stdio standards)
}

And just specialize it however you want, where it matters (here you can also add whatever rules you want, maybe even a rule/policy parameter?):

inline bool lexicalConversion(const juce::String & from, double & to)
{
    double output;
    char * endPtr = nullptr;
    output = strtod(from.getCharPointer(), &endPtr);
    if (endPtr > from.getCharPointer())
    {
        to = output;
        return true;
    }
    return false;
}