Is that an Int?

I had to do a little playing around today as I have to validate if a number is an integer? Well there are as many ways to do this as there are perl programmers.

At first I though that is easy just look for the '.' in the string so I gave this a try

if (index($a,'.') == -1 )

but wait what if $a was not a valid number say '0.1.1.1'. All I am really checking for is the '.' so I would have to check if it is a number first. We do have a nifty little util called 'looks_like_number' so we could do this

use Scalar::Util qw(looks_like_number);
if (index($a,'.') == -1 and (!Scalar::Util::looks_like_number($a))

not the most readable code out there.

How about this old gem I remember using with GW-Basic. Simply test subtracting the number from itself as an integer like this and since this is perl we can play with the 'unless'

unless((($x-int($x)))

or if you like

if (!(($x-int($x))))

well that works nice enough but I am back to square one again as string value will always come up as an int as well the number that ends with a '.' like '10.0' will come up as an int as well. Which is good until you try to insert it into a table and what ever puts this into the db decides that '10.0' is the same as '10.00000000001' and dis-allows the insert.

So how about our old friend the regex?? personally I am not much of a fan of them but they do have their uses so the simplest one is just

if ( $a =~/\D/)

any character not a digit. Hey two birds with one stone as it checks to see if it is a number. Well at least I thought I had it but, '-1', '+42' and even 0b1000101 are valid numbers at least to perl. Well in my case I discovered after I release the code that a negative int was indeed valid input, so back to the electronic brain.

Well lets have a closer look at the regex we may need. I need to start with either a '+' or '-'. So I will start to test for my sign '(\+|-) , now I need only test for numbers "?\d+" so this should work

if ( $a =~/(\+|-)?\d+/)

But alas it failed when I gave it something goofy like this '+2020fff'. Ah but that is when I remembered the anchor expression '^' start from the beginning and '$' continue to the end, and now I go this

if ( $a =~/^(\+|-)?\d+$/)

and if seem to work nicely and it even works with 0b1000101 but not '0b1000101'

So that is what I am going with.


4 Comments

Using \D can be dangerous in the unicode world:


use utf8;
print "໖" =~ /\d/;

use Types::Standard qw(Int);
 
# Returns $x if $x is an integer;
# throws an exception otherwise.
Int->( $x );
 
# Just returns a boolean; doesn't
# throw an exception.
Int->check( $x );

Using \D can be dangerous in the unicode world:

use utf8; print "໖" =~ /\d/a;

Take a second look at looks_like_number(), because it's fast (at least twice faster than your last regex). And it *can* differentiate between ints/floats/stringy numbers/inf/nan. http://blogs.perl.org/users/steven_haryanto/2013/10/scalarutillooks-like-number.html

Leave a comment

About byterock

user-pic Long time Perl guy, a few CPAN mods allot of work on DBD::Oracle and a few YAPC presentations