Perl通常会自动透明地将数字转换为字符串值,反之亦然。但是肯定有某种方法允许例如Data::Dumper
区分二者,就像这个示例中的情况:
use Data::Dumper;
print Dumper('1', 1);
# output:
$VAR1 = '1';
$VAR2 = 1;
是否有Perl函数可以让我以类似的方式区分标量值是存储为数字还是字符串?
标量有许多不同的字段。当使用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'";
}
检查:
($sv->FLAGS & SVf_IOK) && !($sv->FLAGS & SVf_IVisUV)
($sv->FLAGS & SVf_IOK) && ($sv->FLAGS & SVf_IVisUV)
$sv->FLAGS & SVf_NOK
($sv->FLAGS & SVf_POK) && !($sv->FLAGS & SVf_UTF8)
($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
这更加复杂。根据变量所用的上下文,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无法找到这个信息。Data::Dumper使用C库来实现。如果被强制使用Perl,则无法区分类似十进制数字的字符串和数字。
use Data::Dumper;
$Data::Dumper::Useperl = 1;
print Dumper(['1',1])."\n";
#output
$VAR1 = [
1,
1
];
根据您的评论,这是为了确定是否需要引用SQL语句,我想说正确的解决方案是使用占位符,这在DBI文档中有描述。
通常情况下,您不应直接将变量插入到查询字符串中。
有一个简单的解决方案没有被提到,那就是Scalar::Util的looks_like_number。Scalar::Util是自5.7.3以来的核心模块,looks_like_number使用perlapi来确定标量是否为数字。
当变量被用作数字时,会导致该变量在后续上下文中被视为数值。然而,反过来并非完全如此,就像这个例子所展示的:
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";}
Params::Util::_NUMBER
:use Params::Util qw<_NUMBER>;
unless ( _NUMBER( $scalar ) or $scalar =~ /^'.*'$/ ) {
$scalar =~ s/'/''/g;
$scalar = "'$scalar'";
}
sub is_numeric {
my $value = shift;
no warnings 'numeric';
# string & "" -> ""
# number & "" -> 0 (with warning)
# nan and inf can detect as numbers, so check with * 0
return unless length((my $dummy = "") & $value);
return unless 0 + $value eq $value;
return 1 if $value * 0 == 0; # finite number
return -1; # inf or nan
}
我认为没有perl函数可以找到值的类型。我们可以找到DS(标量,数组,哈希)的类型。可以使用正则表达式查找值的类型。
FOO = 00023
与FOO ='00023'
的区别。 - Stefan Majewsky