20

Perl 通常将数字转换为字符串值,反之亦然。然而,必须有一些东西允许例如Data::Dumper区分两者,如本例所示:

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

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

是否有一个 Perl 函数允许我以类似的方式区分标量的值是存储为数字还是字符串?

4

9 回答 9

19

标量有许多不同的字段。当使用 Perl 5.8 或更高版本时,Data::Dumper 检查 IV(整数值)字段中是否有任何内容。具体来说,它使用类似于以下内容的内容:

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'";
}

检查:

  • 有符号整数 (IV):($sv->FLAGS & SVf_IOK) && !($sv->FLAGS & SVf_IVisUV)
  • 无符号整数 (IV):($sv->FLAGS & SVf_IOK) && ($sv->FLAGS & SVf_IVisUV)
  • 浮点数 (NV):$sv->FLAGS & SVf_NOK
  • 降级字符串(PV):($sv->FLAGS & SVf_POK) && !($sv->FLAGS & SVf_UTF8)
  • 升级字符串(PV):($sv->FLAGS & SVf_POK) && ($sv->FLAGS & SVf_UTF8)

你可以使用类似的技巧。但请记住,

  • 将浮点数字符串化而不丢失是非常困难的。

  • 您需要正确转义字符串文字中的某些字节(例如 NUL)。

  • 一个标量可以存储多个值。例如,!!0包含一个字符串(空字符串)、一个浮点数 ( 0) 和一个有符号整数 ( 0)。如您所见,不同的值甚至并不总是相等的。有关更戏剧性的示例,请查看以下内容:

      $ perl -E'open($fh, "non-existent"); say for 0+$!, "".$!;'
      2
      No such file or directory
    
于 2012-10-02T16:02:43.163 回答
16

它更复杂。Perl 根据使用变量的上下文改变变量的内部表示:

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
于 2012-10-02T08:16:41.043 回答
11

使用纯 perl 无法找到这一点。Data::Dumper 使用一个 C 库来实现它。如果被迫使用 Perl,如果字符串看起来像十进制数字,它就不会区分字符串和数字。

use Data::Dumper;
$Data::Dumper::Useperl = 1;
print Dumper(['1',1])."\n";

#output
$VAR1 = [
          1,
          1
        ];
于 2012-10-02T08:18:43.137 回答
6

根据您的评论,这是为了确定 SQL 语句是否需要引用,我想说正确的解决方案是使用 DBI 文档中描述的占位符。

通常,您不应直接在查询字符串中插入变量。

于 2012-10-02T14:01:04.780 回答
4

一个没有提到的简单解决方案是 Scalar::Util 的looks_like_number。Scalar::Util 是自 5.7.3 以来的核心模块,looks_like_number 使用perlapi来确定标量是否为数字。

于 2012-10-08T16:43:38.467 回答
4

autoboxautobox::universal附带的模块提供了一个可用于此目的的功能:type

use autobox::universal qw(type);

say type("42");  # STRING
say type(42);    # INTEGER
say type(42.0);  # FLOAT 
say type(undef); # UNDEF 
于 2014-07-23T19:27:00.680 回答
3

当变量用作数字时,这会导致变量在后续上下文中被假定为数字。然而,反过来并不完全正确,如本例所示:

use Data::Dumper;

my $foo = '1';
print Dumper $foo;  #character
my $bar = $foo + 0;
print Dumper $foo;  #numeric
$bar = $foo . ' ';
print Dumper $foo;  #still numeric!
$foo = $foo . '';
print Dumper $foo;  #character

人们可能期望第三个操作$foo放回字符串上下文(reversing $foo + 0),但事实并非如此。

如果您想检查某物是否是数字,标准方法是使用正则表达式。您检查的内容因您想要的数字而异:

if ($foo =~ /^\d+$/)      { print "positive integer" }
if ($foo =~ /^-?\d+$/)    { print "integer"          }
if ($foo =~ /^\d+\.\d+$/) { print "Decimal"          }

等等。

检查内部存储的方式通常没有用——您通常不需要担心这一点。但是,如果您想复制 Dumper 在这里所做的事情,那没问题:

if ((Dumper $foo) =~ /'/) {print "character";}

如果 Dumper 的输出包含单引号,则表示它显示的是一个以字符串形式表示的变量。

于 2012-10-02T08:32:25.160 回答
3

您可能想尝试Params::Util::_NUMBER

use Params::Util qw<_NUMBER>;

unless ( _NUMBER( $scalar ) or $scalar =~ /^'.*'$/ ) { 
   $scalar =~ s/'/''/g;
   $scalar = "'$scalar'";
}
于 2012-10-02T14:48:51.840 回答
-1

我认为没有 perl 函数可以找到值的类型。可以找到 DS(scalar,array,hash) 的类型。可以使用正则表达式来查找值的类型。

于 2012-10-02T08:22:55.657 回答