String 如何在Perl中区分数字标量和字符串标量?

String 如何在Perl中区分数字标量和字符串标量?,string,perl,types,integer,String,Perl,Types,Integer,Perl通常透明地将数字值转换为字符串值,反之亦然。然而,必须有某种东西,例如,Data::Dumper能够区分两者,如本例所示: use Data::Dumper; print Dumper('1', 1); # output: $VAR1 = '1'; $VAR2 = 1; 是否有一个Perl函数允许我以类似的方式区分标量的值是存储为数字还是存储为字符串?它更复杂。Perl根据变量在其中使用的上下文更改变量的内部表示形式: perl -MDevel::Peek -e ' $x =

Perl通常透明地将数字值转换为字符串值,反之亦然。然而,必须有某种东西,例如,
Data::Dumper
能够区分两者,如本例所示:

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

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

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

它更复杂。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

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

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

#output
$VAR1 = [
          1,
          1
        ];

我认为没有perl函数来查找值的类型。可以找到DS的类型(标量、数组、散列)。可以使用正则表达式查找值的类型。

当变量用作数字时,会导致在后续上下文中假定该变量为数字。然而,情况并非完全相反,如本例所示:

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
放回字符串上下文中(反转
$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";}

如果转储程序的输出包含一个引号,这意味着它正在显示一个以字符串形式表示的变量。

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

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

您可能需要尝试:

使用Params::Util qw;
除非(_NUMBER($scalar)或$scalar=~/^.*.$/){
$scalar=~s/'/''/g;
$scalar=“$scalar'”;
}

标量有许多不同的字段。当使用Perl5.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'";
}
  $ perl -E'open($fh, "non-existent"); say for 0+$!, "".$!;'
  2
  No such file or directory
你可以使用类似的技巧。但请记住

  • 要在不丢失的情况下对浮点数进行字符串化是非常困难的。(使用
    $sv->FLAGS和SVf_NOK
    识别浮动指针编号)

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

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

    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'";
    }
    
      $ perl -E'open($fh, "non-existent"); say for 0+$!, "".$!;'
      2
      No such file or directory
    

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

      随附的
      autobox::universal
      模块提供了一个可用于此目的的函数:

      使用autobox::通用qw(类型);
      说类型(“42”)#一串
      说类型(42);#整数
      说类型(42.0);#浮动
      说类型(未定义);#未定义
      
      在什么情况下您需要知道差异?我想构建SQL条件,并区分需要引用的值和不需要引用的值。想想看
      FOO=00023
      vs.
      FOO='00023'
      。为什么不使用占位符,让您的DBI模块担心引用?是的,我希望如此,但DBI被这个代码库中的抽象层隐藏了。好吧,这很愚蠢。有很多方法可以确定或强制变量成为你想要的,但是,由于信息太少,很难给你建议。我接受了这个方法,因为它清楚地表明我无法解决我的问题。其他答案对他们的理解仍然很有帮助。事实上,不需要编写C代码或安装任何模块就可以实现。看看我的答案。哇,我不知道,谢谢!我很好奇为什么在Data::Dumper中没有使用它,他们为此使用了普通的正则表达式。