Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

detecting of scalar is string or numeric

by vkhera (Novice)
on Feb 08, 2017 at 13:43 UTC ( #1181405=perlquestion: print w/replies, xml ) Need Help??

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

Up until 5.24 where it was deprecated, one could safely use
$x ^ $x
to determine if the value of $x is a string or a numeric value. However, now, if your string contains high code points (like emoji characters) perl issues a deprecation warning (see the last line in this snippet)
% perl -Mutf8 -e '$x = 42; if ($x ^ $x) {print "string\n";}' % perl -Mutf8 -e '$x = 42.24; if ($x ^ $x) {print "string\n";}' % perl -Mutf8 -e '$x = "42"; if ($x ^ $x) {print "string\n";}' string % perl -Mutf8 -e '$x = "00042"; if ($x ^ $x) {print "string\n";}' string % perl -Mutf8 -e '$x = "42\N{BLACK TELEPHONE}"; if ($x ^ $x) {print "s +tring\n";}' Use of strings with code points over 0xFF as arguments to bitwise xor +(^) operator is deprecated at -e line 1. string % perl -v This is perl 5, version 24, subversion 1 (v5.24.1) built for amd64-fre +ebsd-thread-multi
Since it still works, for now I'm just turning off that warning, but I need to find a replacement test. The Scalar::Util::looks_like_number() function doesn't do the same thing in that it considers the string form of "42" not to be a string in my test above. The purpose of this is to conditionally do utf8 encoding of a string for use with XMLRPC. The XMLRPC libraries need to detect if the variable holds a number or a string and generate the XML accordingly. I do not want to stringify numbers where I do not need to. I have not been able to find a replacement to this test searching this site and others. What should I do?

Replies are listed 'Best First'.
Re: detecting of scalar is string or numeric
by Corion (Pope) on Feb 08, 2017 at 13:56 UTC

    This check has always been highly fragile (sorry for the Windows command line quoting):

    perl -Mutf8 -e "$x = qq(42); if( 0+$x ) {}; if ($x ^ $x) {print qq(str +ing\n);}"

    That oneliner outputs nothing thus considers $x not a string even if $x was never "modified". A simple check of a variable can change what "value type" your test considers that variable to contain.

Re: detecting of scalar is string or numeric
by haukex (Chancellor) on Feb 08, 2017 at 13:58 UTC
Re: detecting of scalar is string or numeric
by syphilis (Bishop) on Feb 08, 2017 at 23:36 UTC
    if ($x ^ $x) {print "string\n";}

    Seems to me that this will print "string" when and only when neither the NOK or IOK flag is set.
    That being so, an alternative is to check whether the IOK or NOK flag is set - for which a module probably exists, or you could do it via XS or Inline::C.
    use strict; use warnings; use Inline C => Config => BUILD_NOISY => 1, ; use Inline C => <<'END_C'; int foo(SV * in) { if(SvIOK(in) || SvNOK(in)) return 1; return 0; } END_C my $num = 42; my $str = "42"; my $ref = \$num; my $nv = 42.42; my $dual = "42"; # POK flag set. if($dual > 0){} # IOK flag also set print foo($num), " ", xor_check($num), "\n"; print foo($str), " ", xor_check($str), "\n"; print foo($ref), " ", xor_check($ref), "\n"; print foo($nv) , " ", xor_check($nv) , "\n"; print foo($dual), " ", xor_check($dual), "\n"; sub xor_check { return 0 if($_[0] ^ $_[0]); return 1; } __END__ Outputs (perl-5.24.0): 1 1 0 0 0 0 1 1 1 1
    Now, you might be able to find a variable for which foo() and xor_check() return different values.
    I'm certainly interested to know if someone can provide such an example.

    Cheers,
    Rob
Re: detecting of scalar is string or numeric
by clueless newbie (Deacon) on Feb 09, 2017 at 16:03 UTC
Re: detecting of scalar is string or numeric (SvTYPE)
by Anonymous Monk on Feb 08, 2017 at 23:34 UTC

    Its better not to do detection, but simply force numification if you're expecting a number, and let warnings issue a warning

    #!/usr/bin/perl -- use strict; use warnings; use B(); sub SvTYPE (\[$@%&*]) { use B(); ref(B::svref_2object($_[0])) } { my $x = 42; TYPE(\$x); } { my $x = 42.24; TYPE(\$x); } { my $x = "42.24"; TYPE(\$x); $x=$x+0; print SvTYPE($x), "\n"; } { my $x = "00042"; TYPE(\$x); $x=$x+0; print SvTYPE($x), "\n"; } { my $x = "42\x{260E}"; TYPE(\$x); } sub TYPE { no warnings; print $_[0],' = ', ${$_[0]},' = ', ref(B::svref_2object($_[0])), " +\n"; } __END__
    SCALAR(0xae664c) = 42 = B::IV
    SCALAR(0xae637c) = 42.24 = B::NV
    SCALAR(0xae633c) = 42.24 = B::PV
    B::PVNV
    SCALAR(0xae627c) = 00042 = B::PV
    B::PVIV
    SCALAR(0xae609c) = 42☎ = B::PV
    

      That doesn't help.

      Those class names just tell you the type of scalar, but nothing about the value within.

      For example, if you get B::IV, all you know is that the scalar contains one of the following:

      • Nothing (undef)
      • A signed integer
      • An unsigned integer
      • A reference

      For example, if you get B::PVIV, all you know is that the scalar contains one of the following:

      • Nothing (undef)
      • A signed integer
      • An unsigned integer
      • A string
      • A reference
      • A signed integer and a string
      • An unsigned integer and a string

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1181405]
Approved by Corion
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (5)
As of 2020-03-30 20:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    To "Disagree to disagree" means to:









    Results (176 votes). Check out past polls.

    Notices?