Ruby: How to check if a String is numeric
Finally, a technical article… This one caused by a seemingly simple question on Stack Overflow, asking how to Test if string is a number.
It turns out that it’s not entirely that simple to answer, and there are lot of apparently correct ideas, that all turn out to fail closer scrutiny.
For those who just want the answer: As far as I can tell, the best method is the one proposed on RosettaCode and in the Stack Overflow question above:
class String
def numeric?
Float(self) != nil rescue false
end
end
If you want some more details, continue reading.
Let’s verify our code by transforming the question to something ubiquitous. The above method String#numeric?
has the following behaviour:
"42".numeric? #=> true
"-42".numeric? #=> true
"1.2".numeric? #=> true
"0".numeric? #=> true
"1.2e34".numeric? #=> true
"1_000".numeric? #=> true
"".numeric? #=> false
" ".numeric? #=> false
"a".numeric? #=> false
"-".numeric? #=> false
".".numeric? #=> false
"_".numeric? #=> false
"1.2.3".numeric? #=> false
As far as I can tell, that’s what we want. Are there any cases I’ve missed?
Performance
The method uses a rescue clause, basically for flow control. In addition to this being a dubious practice at best, it is not the best-performing method. In raw numbers it takes more than 10 times as long to handle a failing case than a successful one (on Ruby 1.8.7).
This causes some people to suggest a regular expression driven approach instead.
Using a regular expression
Digging around on the interwebs I found a regex discussed on RailsForum that we can use – at least after modifying it somewhat:
def numeric?
match(/\A[+-]?\d+?(_?\d+)*(\.\d+e?\d*)?\Z/) == nil ? false : true
end
This behaves as expected. Unfortunately, it is awfully slow for failing strings, way worse than using the Float rescue method above.
Conclusion
In short, to check if a string is a numeric value, use this method:
class String
def numeric?
Float(self) != nil rescue false
end
end
It is correct, generally faster, and definitely more readable.