http://www.perlmonks.org?node_id=791650

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

Hi Monks, I am trying to distinguish between numeric and string scalar values using regular expression as the below code.
my $var = { 'key1' => '1', 'key2' => 1 }; print "Key1: It is a number\n" if ( $var->{'key1'} =~ /^-?\d/ ) ; print "Key2: It is a number\n" if ( $var->{'key2'} =~ /^-?\d/ ) ;
But it is always getting interpreted as a number. Could somebody help me distinguish between the values '1' and 1 as in the above code

Replies are listed 'Best First'.
Re: How to check if a scalar value is numeric or string?
by ikegami (Patriarch) on Aug 27, 2009 at 14:56 UTC

    But it is always getting interpreted as a number.

    Actually, they're both getting interpreted as strings. The regex match operator only works on strings, so if it's operand isn't a string, it automatically converts the operand to a string.

    Then you use the match operator to check if the string contains something that looks like a number. And since it does, you print your message.

    As for the answer to your question, Perl doesn't see the difference you see. If it needs a string, it automatically converts the number into a string (and the variable then holds both a number and a string). If it needs a number, it automatically converts the string into a number (and the variable then holds both a number and a string). It has no compunction against doing so on a while, even for constants.

Re: How to check if a scalar value is numeric or string?
by biohisham (Priest) on Aug 27, 2009 at 15:21 UTC
    I tried to do it using regular expressions but I couldn't have a clear shot, there is another feature, use the complement operator, In order to determine if a variable is a number or a string, employ the principle that &ing a variable with its complement should give 0 as a result if that variable holds a number, whereas it is not the case when that variable is a string.

    try extending on this principle, here is a snippet:

    my $x=111; my $y="111"; print "\$x is a number\n" if !($x & ~$x); print "\$y is a string\n" if ($y & ~$y);

    Excellence is an Endeavor of Persistence. Chance Favors a Prepared Mind.

      By the way, $x & ~$x can also be written as $x ^ $x.

        As of perl 5.24 you can no longer use this trick because ^ does not like strings with code points over 0xFF. Right now it is deprecated, but will presumably be fatal later.
      Many thanks, you saved my day, this approach works perfectly for me.

        But watch out for side effects.

        my $x = 1; my $y = '1'; print "x is a ", (($x ^ $x)?"string":"number"), "\n"; print "y is a ", (($y ^ $y)?"string":"number"), "\n"; print "\$x = $x\n"; print "\$y = $y\n"; print "x is a ", (($x ^ $x)?"string":"number"), "\n"; print "y is a ", (($y ^ $y)?"string":"number"), "\n"; my $z = $x + $y; print "\$z = $z\n"; print "x is a ", (($x ^ $x)?"string":"number"), "\n"; print "y is a ", (($y ^ $y)?"string":"number"), "\n";

        gives

        x is a number y is a string $x = 1 $y = 1 x is a number y is a string $z = 2 x is a number y is a number

        It might be better to revise your program so that it doesn't depend on such a subtle distinction.

Re: How to check if a scalar value is numeric or string?
by SuicideJunkie (Vicar) on Aug 27, 2009 at 14:47 UTC

    There is no practical difference between the values of '1' and 1. You could possibly dig into the guts of SVs and test whether your variable has been used as a number/string yet, but that would be a lot of work for questionable gains.

    The real (XY problem) question is; why do you care? Perl will give you a string where appropriate (concatenation operation, say), and a number where appropriate (eg: addition operation)

Re: How to check if a scalar value is numeric or string?
by james2vegas (Chaplain) on Aug 27, 2009 at 14:38 UTC
    The looks_like_number function in Scalar::Util would tell you if Perl thinks your scalar is numeric.

    Update: Ok, guess not that, what is your need for this functionality? Strings that look like numbers and numbers are generally interchangeable.
      Generally, yes. In a few cases, there's a difference. Binary bit operators, for instance.
      say "12" | "34"; # 36 say 12 | 34; # 46
      And you might think the following code won't give you a divide by zero error:
      if ($num && $num =~ /^[0-9]+$/) {say 1/$num}
      but it will if $num eq '00'.

      Devel::Peek is a tool that can tell you whether scalar holds a numeric value or not (look for the IV and NV flags).

      Actually the exact requirement is as below:

      1. Use GetOptions to process the input command line.
      2. Do the required validation. This will result in modifying the command line paramteres
         and resultant will be stored in a hash.
      3. Use this hash in order to form the command line again.
      

      The problem is while doing the 3rd step, I need to distinguish if a given argument is a toggle or a parameter. So, I want to distinguish between 1( toggle ) and '1'( argument ).

        Why don't you save a copy of the command line before calling GetOptions?

        If you can't because you're rearranging the command line somehow, you'll need to pass your reconstructor some of the information you pass to GetOptions (as in, whether a parameter is a toggle or takes a value).

        Your requirement mentions GetOptions. Does this mean you are using Getopt::Long? If so, how are you specifying the option? opt=s, opt:s, opt=i, opt:i, opt, etc?

        Getopt::Long is usually good about setting sane values. If you asked for an integer with :i, it will set your value to 0 if there was no argument given, so you can test with exists to see if it was set.

        $,=' ';$\=',';$_=[qw,Just another Perl hacker,];print@$_;

        If you know at the point where you store the value that it is a 1 or a '1', then store '1' (including the quote marks) for the string version.


        True laziness is hard work
Re: How to check if a scalar value is numeric or string?
by bluestar (Novice) on Aug 27, 2009 at 15:31 UTC
    This should give you a fairly accurate answer i.e. suppress warnings locally and compare the value to itself after adding a numeric value to it.
    use strict; use warnings; my $integer = 777; my $string = 'hello'; my $float = 3.22 + 4.34; print _isNumeric($integer) . "\n"; print _isNumeric($string) . "\n"; print _isNumeric($float) . "\n"; sub _isNumeric { my ($value) = @_; no warnings; return 1 if ($value + 0) eq $value; return 0; }
      That's a pretty good reimplementation of looks_like_number, but it doesn't answer the OP's question. He still gets the same answer he currently gets.
      sub _isNumeric { my ($value) = @_; no warnings; return 1 if ($value + 0) eq $value; return 0; } my $var = { 'key1' => '1', 'key2' => 1 }; print "Key1: It is a number\n" if _isNumeric( $var->{'key1'} ) ; print "Key2: It is a number\n" if _isNumeric( $var->{'key2'} ) ;
      Key1: It is a number Key2: It is a number
Re: How to check if a scalar value is numeric or string?
by syphilis (Archbishop) on Aug 28, 2009 at 02:38 UTC
    There may be a module that already implements this:
    use warnings; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'EOC'; SV* wotisit(SV * arg) { if(SvPOK(arg)) return newSVpv("string", 0); if(SvIOK(arg) || SvNOK(arg) || SvUOK(arg)) return newSVpv("number", 0); return newSVpv("something else", 0); } EOC use Math::BigInt; # for testing my $o = Math::BigInt->new(0); for('1', '1.1', 1, 1.1, $o) { print wotisit($_), "\n"; }
    Outputs:
    string string number number something else
    Cheers,
    Rob
Re: How to check if a scalar value is numeric or string?
by ibravos (Initiate) on Mar 09, 2012 at 10:52 UTC
    Thanks to the sugestions above i have the function isnum:
    sub isnum ($) { $_[0] ^ $_[0] ? 0 : 1 }
    XORing a number gives zero, so the function returns 1. XORing a string gives a string of nulls, still a non-empty string, so the function returns 0
      Correction: isnum('') returns 1, so here is a better version
      sub isnum ($) { return 0 if $_[0] eq ''; $_[0] ^ $_[0] ? 0 : 1 }