Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

How to capture the "isn't numeric" warning?

by harangzsolt33 (Chaplain)
on Jun 14, 2019 at 23:59 UTC ( [id://11101373]=perlquestion: print w/replies, xml ) Need Help??

harangzsolt33 has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks, How can I quickly decide whether a variable holds a number or not a number?

When I run the following code, it throws a warning saying that Argument "abc" isn't numeric in int at C:\PERL\test.pl line 8. Of course, it isn't numeric! But how do I capture this warning and make my code do something different if it isn't numeric?

I don't want to use a regex or various string tests to see if the variable contains any non-numeric characters, because if it's a number, then perl has to convert the number to a string, and then it has to inspect each character of the string. I don't want to go down that route. Is there a faster way to test if a variable is a number?

#!/usr/bin/perl -w use strict; use warnings; my $X = "abc"; $X = int($X); print $X;

Replies are listed 'Best First'.
Re: How to capture the "isn't numeric" warning?
by Cristoforo (Curate) on Jun 15, 2019 at 00:16 UTC
      You can get the same answer directly from the FAQ.
      perldoc -q "How do I determine whether a scalar is a number"
      Bill
Re: How to capture the "isn't numeric" warning?
by LanX (Saint) on Jun 15, 2019 at 00:32 UTC
    > how do I capture this warning and make my code do something different if it isn't numeric?

    One approach is to locally change the SIG handler

    my $result; { my $flag; local $SIG{__WARN__} = sub { # catch error in @_, set $flag }; $result = int($x); something_different($x,$result) if $flag; }

    See Perl's Warn and Die Signals for a general discussion.

    HTH! :)

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      Oh, wow, THANK YOU! That's exactly what I wanted to know. So, what do you think of my sub? Lol It seems to work...

      #!/usr/bin/perl -w use strict; use warnings; print isNumber("abc"); # returns 0 print "\n"; print isNumber("123"); # returns 1 print "\n"; sub isNumber { @_ or return 0; my $N = shift; defined $N or return 0; my $R = 1; { local $SIG{__WARN__} = sub { $R = 0; }; $N = int($N); } return $R; }
        That's exactly what I wanted to know. So, what do you think of my sub?

        Sorry, but I don't think it's a good idea. For one, the signal handler will fire on any warning. Scalar::Util's looks_like_number (a core module) calls the internal Perl function that checks if a string looks like a number, so this is just a really convoluted way of calling that function. In your OP, you said "Is there a faster way to test if a variable is a number?", and this is definitely a much slower way to do so - in fact, roughly 38 times slower! As numerous people have said, just use looks_like_number.

        use warnings; use strict; use Benchmark qw/cmpthese/; use Scalar::Util qw/looks_like_number/; sub isNumber { @_ or return 0; my $N = shift; defined $N or return 0; my $R = 1; { local $SIG{__WARN__} = sub { $R = 0; }; $N = int($N); } return $R; } cmpthese(-2, { isNumber => sub { isNumber("123") or die; isNumber("-5e7") or die; isNumber("abc") and die; isNumber("") and die; }, looks_like_number => sub { looks_like_number("123") or die; looks_like_number("-5e7") or die; looks_like_number("abc") and die; looks_like_number("") and die; } }); __END__ Rate isNumber looks_like_number isNumber 153840/s -- -97% looks_like_number 5991731/s 3795% --

        I agree with syphilis here (update: and haukex here) that Scalar::Util::looks_like_number() looks like a better solution. One problem with this is that it's dependent upon the state of warnings in the enclosing scope:

        c:\@Work\Perl\monks>perl -le "use strict; use warnings; ;; no warnings 'numeric'; print 'numeric warning DISabled'; ;; print q{'abc' }, isNumber('abc'); print q{'123' }, isNumber('123'); print q{123 }, isNumber(123); ;; sub isNumber { @_ or return 0; my $N = shift; defined $N or return 0; my $R = 1; { local $SIG{__WARN__} = sub { $R = 0; }; $N = int($N); } return $R; } " numeric warning DISabled 'abc' 1 '123' 1 123 1
        A fix (if you're wedded to isNumber()) might be something like:
        c:\@Work\Perl\monks>perl -le "use strict; use warnings; ;; no warnings 'numeric'; print 'numeric warning DISabled'; ;; print q{'abc' }, isNumber('abc'); print q{'123' }, isNumber('123'); print q{123 }, isNumber(123); print q{undef }, isNumber(undef); print q{() }, isNumber(); ;; sub isNumber { my $numeric = 1; use warnings 'numeric'; local $SIG{__WARN__} = sub { $numeric = 0; }; my $x = int $_[0]; return $numeric; } " numeric warning DISabled 'abc' 0 '123' 1 123 1 undef 0 () 0
        A test plan: More test cases would be wise: there are no negatives, +0, +1, '+0', '+1', +123e+45, etc.


        Give a man a fish:  <%-{-{-{-<

        Hi,

        I think isNumber() is doing the same as Scalar::Util::looks_like_number(), except that the former is returning '0' for FALSE, whereas the latter returns '' (the empty string) for FALSE.
        One slight advantage I can see in using looks_like_number() is that it doesn't require the warnings pragma.
        And, I think, isNumber() is rather convoluted in that int($N) is going to perform essentially the same check as looks_like_number() and then catch the warning if that check fails, before assigning 0 to $R and returning. So it's also a little less efficient.

        But that just makes isNumber() the more interesting solution !

        Cheers,
        Rob
        Hi harangzsolt33,

        It really depends on the use case, you haven't been too clear about this.

        What I've shown is a kind of exception handling, (the difference to hippo's solution being that it is only catching warnings and not fatals)

        That's ok if you want to monitor complicated code or volatile input.

        Or if you have to deal with different unpredictable warnings.

        > So, what do you think of my sub?

        In this particular case I (like the others replying) have problems to see the advantage over Scalar::Util 's looks_like_number().

        Unless you can show a case where the latter fails to avoid the warning.

        And you should at least check which warnings were triggered.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: How to capture the "isn't numeric" warning?
by hippo (Bishop) on Jun 15, 2019 at 09:32 UTC

    A solution with Try::Tiny:

    #!/usr/bin/env perl use strict; use warnings; use Try::Tiny; my $X = "abc"; try { use warnings FATAL => 'numeric'; $X = int($X); } catch { $X = "This should have been a number"; }; print $X;

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11101373]
Approved by LanX
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (2)
As of 2024-04-26 00:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found