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

> 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

Replies are listed 'Best First'.
Re^2: How to capture the "isn't numeric" warning?
by harangzsolt33 (Deacon) on Jun 15, 2019 at 04:37 UTC
    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% --
        For one, the signal handler will fire on any warning.

        Yes, the same thought occurred to me ... but then I couldn't come up with an argument that could be passed to isNumeric() && elicit a warning other than the "isn't numeric" one.
        Is there such an argument ?

        Cheers,
        Rob

      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

        whereas the latter returns '' (the empty string) for FALSE.

        It does not.

        $ perl -MScalar::Util=looks_like_number -we' CORE::say 0+""; ' Argument "" isn't numeric in addition (+) at -e line 2. 0 $ perl -MScalar::Util=looks_like_number -we' CORE::say 0+looks_like_number("abc"); ' 0

        It returns the usual empty string, 0, 0.0 triple-var Perl uses for false (sv_no).

      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

        Hmm... All right, so it looks like looks_like_number() is better. Usually when I read this forum or ask something, I always learn something and then something extra on top of that. In this case, I learned how to capture warnings, how to turn off warnings. (And don't laugh, my assumption was that you can't just create a block { ... } without having a "for" or "sub" or an "if" keyword in front of it. In other programming languages, you get an error if you just create a block with no keyword in front of it. In perl, this is allowed, and it is something that I just discovered. Lol)

        Thank you for your informative answers!!!