Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell apart numeric scalars and string scalars in Perl?

Tags:

Perl usually converts numeric to string values and vice versa transparently. Yet there must be something which allows e.g. Data::Dumper to discriminate between both, as in this example:

use Data::Dumper;
print Dumper('1', 1);

# output:
$VAR1 = '1';
$VAR2 = 1;

Is there a Perl function which allows me to discriminate in a similar way whether a scalar's value is stored as number or as string?

like image 781
Stefan Majewsky Avatar asked Oct 02 '12 07:10

Stefan Majewsky


People also ask

How do I compare two scalars in Perl?

== and eq: This operator is used to check the equality. In the following code, outputs of codes after using == and eq are compared and show how it works for numeric and string scalars differently.

What are scalars in Perl?

Advertisements. A scalar is a single unit of data. That data might be an integer number, floating point, a character, a string, a paragraph, or an entire web page.

Is a string a scalar variable?

Scalar variables can be either a number or a string -- What might seem confusing at first sight actually makes a lot of sense and can make programming a lot easier.

Which of these is a type of the scalar in Perl?

Arrays are ordered lists of scalars that you access with a numeric index, which starts with 0. They are preceded by an "at" sign (@). Hashes are unordered sets of key/value pairs that you access using the keys as subscripts.


2 Answers

A scalar has a number of different fields. When using Perl 5.8 or higher, Data::Dumper inspects if there's anything in the IV (integer value) field. Specifically, it uses something similar to the following:

use B qw( svref_2object SVf_IOK );

sub create_data_dumper_literal {
    my ($x) = @_;  # This copying is important as it "resolves" magic.
    return "undef" if !defined($x);

    my $sv = svref_2object(\$x);
    my $iok = $sv->FLAGS & SVf_IOK;
    return "$x" if $iok;

    $x =~ s/(['\\])/\\$1/g;
    return "'$x'";
}

Checks:

  • Signed integer (IV): ($sv->FLAGS & SVf_IOK) && !($sv->FLAGS & SVf_IVisUV)
  • Unsigned integer (IV): ($sv->FLAGS & SVf_IOK) && ($sv->FLAGS & SVf_IVisUV)
  • Floating-point number (NV): $sv->FLAGS & SVf_NOK
  • Downgraded string (PV): ($sv->FLAGS & SVf_POK) && !($sv->FLAGS & SVf_UTF8)
  • Upgraded string (PV): ($sv->FLAGS & SVf_POK) && ($sv->FLAGS & SVf_UTF8)

You could use similar tricks. But keep in mind,

  • It'll be very hard to stringify floating point numbers without loss.

  • You need to properly escape certain bytes (e.g. NUL) in string literals.

  • A scalar can have more than one value stored in it. For example, !!0 contains a string (the empty string), a floating point number (0) and a signed integer (0). As you can see, the different values aren't even always equivalent. For a more dramatic example, check out the following:

      $ perl -E'open($fh, "non-existent"); say for 0+$!, "".$!;'
      2
      No such file or directory
    
like image 99
ikegami Avatar answered Sep 24 '22 02:09

ikegami


It is more complicated. Perl changes the internal representation of a variable depending on the context the variable is used in:

perl -MDevel::Peek -e '
    $x = 1;    print Dump $x;
    $x eq "a"; print Dump $x;
    $x .= q(); print Dump $x;
'
SV = IV(0x794c68) at 0x794c78
  REFCNT = 1
  FLAGS = (IOK,pIOK)
  IV = 1
SV = PVIV(0x7800b8) at 0x794c78
  REFCNT = 1
  FLAGS = (IOK,POK,pIOK,pPOK)
  IV = 1
  PV = 0x785320 "1"\0
  CUR = 1
  LEN = 16
SV = PVIV(0x7800b8) at 0x794c78
  REFCNT = 1
  FLAGS = (POK,pPOK)
  IV = 1
  PV = 0x785320 "1"\0
  CUR = 1
  LEN = 16
like image 30
choroba Avatar answered Sep 23 '22 02:09

choroba