如何判断一个变量在 Perl 中是不是有数值?
Posted
技术标签:
【中文标题】如何判断一个变量在 Perl 中是不是有数值?【英文标题】:How do I tell if a variable has a numeric value in Perl?如何判断一个变量在 Perl 中是否有数值? 【发布时间】:2010-09-05 23:21:48 【问题描述】:在 Perl 中是否有一种简单的方法可以让我确定给定变量是否为数字?大致如下:
if (is_number($x))
...
将是理想的。在使用-w
开关时不会引发警告的技术当然是首选。
【问题讨论】:
【参考方案1】:使用Scalar::Util::looks_like_number()
,它使用内部Perl C API 的looks_like_number() 函数,这可能是最有效的方法。
请注意,字符串“inf”和“infinity”被视为数字。
示例:
#!/usr/bin/perl
use warnings;
use strict;
use Scalar::Util qw(looks_like_number);
my @exprs = qw(1 5.25 0.001 1.3e8 foo bar 1dd inf infinity);
foreach my $expr (@exprs)
print "$expr is", looks_like_number($expr) ? '' : ' not', " a number\n";
给出这个输出:
1 is a number
5.25 is a number
0.001 is a number
1.3e8 is a number
foo is not a number
bar is not a number
1dd is not a number
inf is a number
infinity is a number
另见:
perldoc Scalar::Util perldoc perlapi 为looks_like_number
【讨论】:
和 perl 文档一样,找到函数的实际定义是相当困难的。追踪perldoc perlapi
告诉我们:测试SV 的内容是否看起来像一个数字(或者是一个数字)。 “Inf”和“Infinity”被视为数字(因此不会发出非数字警告),即使您的 atof() 不了解它们。几乎没有可测试的规范...
Scalar::Util 中的描述很好,looks_like_number 告诉您输入是否是 Perl 将视为数字的内容,这不一定是这个问题的最佳答案。提到 atof 是无关紧要的,atof 不是 CORE:: 或 POSIX 的一部分(您应该查看包含 atof 和 /is/ POSIX 一部分的 strtod)并假设 Perl 认为数字是有效的数字输入to C 函数显然是非常错误的。
我喜欢这个函数,但考虑大整数。 1000000 有很多零需要跟踪,以防出错,但 1,000,000 被视为一个三元素数组,因此 Perl 接受 1_000_000,但looks_like_number() 拒绝。让我难过。
注意:像0x12
这样的十六进制字符串在此测试中不被视为数字。
looks_like_number
公开了internal function,这与 Perl 用于将字符串神奇地转换为数字的方法相同。下划线、十六进制、八进制和二进制数字只允许在源代码中作为文字,而不是字符串,例如0+"0x15"
无法正常工作。 (注意looks_like_number("0 but true")
是真的!:-) reference)【参考方案2】:
最初的问题是如何判断一个变量是否为数字,而不是它是否“具有数值”。
有一些运算符对数字和字符串操作数有不同的操作模式,其中“数字”表示最初是数字或曾经在数字上下文中使用的任何内容(例如,在$x = "123"; 0+$x
中,在加法之前, $x
是一个字符串,之后它被认为是数字)。
一种说法是这样的:
if ( length( do no warnings "numeric"; $x & "" ) )
print "$x is numeric\n";
如果启用了按位功能,这使得&
只是一个数字运算符并添加了一个单独的字符串&.
运算符,您必须禁用它:
if ( length( do no if $] >= 5.022, "feature", "bitwise"; no warnings "numeric"; $x & "" ) )
print "$x is numeric\n";
(按位在 perl 5.022 及更高版本中可用,如果use 5.028;
或更高版本默认启用。)
【讨论】:
如果我将您的例程打包到一个子程序中,我会得到一个奇怪的行为,因为它会正确检测非数字值,直到我尝试第一个数字值,这也被正确检测为 true,但是那么,从那以后的一切都是真的。但是,当我在 length(...) 部分周围放置一个 eval 时,它一直都可以正常工作。知道我错过了什么吗?sub numeric $obj = shift; no warnings "numeric"; return eval('length($obj & "")');
@yogibimbi:您每次都在重用相同的 $obj 变量;试试my $obj = shift;
。为什么要评估?
糟糕,我用了my $obj = shift
,当然,只是没有正确地将它从我的代码转移到评论中,我对其进行了一些编辑。但是,sub numeric my $obj = shift; no warnings "numeric"; return length($obj & "");
会产生相同的错误。当然,拥有一个秘密的全局变量可以解释这种行为,这正是我在这种情况下所期望的,但不幸的是,事情并没有那么简单。此外,这将被strict
和warnings
捕获。我非常绝望地尝试了 eval 以消除错误,并且它起作用了。没有更深层次的推理,只是反复试验。
看看:sub numeric my $obj = shift; no warnings "numeric"; return length($obj & "");
print numeric("w") . "\n"; #=>0
、print numeric("x") . "\n"; #=>0
、print numeric("1") . "\n"; #=>0
、print numeric(3) . "\n"; #=>1
、print numeric("w") . "\n"; #=>1
。如果你在长度周围加上一个 eval(''),最后一个 print 会给出一个 0,就像它应该的那样。去图吧。
@yogibimbi 在我看来很可能你没有运行你认为的代码【参考方案3】:
查看 CPAN 模块 Regexp::Common。我认为它完全符合您的需要并处理所有边缘情况(例如实数、科学记数法等)。例如
use Regexp::Common;
if ($var =~ /$REnumreal/) print qa number;
【讨论】:
【参考方案4】:通常使用正则表达式完成数字验证。此代码将确定某些内容是否为数字,并检查未定义的变量以不引发警告:
sub is_integer
defined $_[0] && $_[0] =~ /^[+-]?\d+$/;
sub is_float
defined $_[0] && $_[0] =~ /^[+-]?\d+(\.\d+)?$/;
这里有一些 reading material 你应该看看。
【讨论】:
我确实认为这有点离题了,尤其是当提问者说 /simple/ 时。许多情况,包括科学记数法,都很难简单。除非将其用于模块,否则我不会担心这些细节。有时简单是最好的。不要把巧克力糖浆放在牛里做巧克力牛奶! '.7' 可能是仍然错过的最简单的情况之一......最好尝试 /^[+-]?\d*\.?\d+$/ 用于浮动。我的变体,也考虑科学记数法: /^[+-]?\d*\.?\d+(?:(?:e|E)\d+)?$/\d*\.?\d+
部分引入了ReDoS 风险。我建议 /^[+-]?(?!\.(?!\d)|$)\d*(?:\.\d*)?$/
或 /^[+-]?(?!\.(?!\d)|$)\d*(?:\.\d*)?(?:(?<=[\d.])e[+-]?\d+)?$/i
包含科学记数法 (explanation and examples)。这使用加倍的负前瞻来防止像 .
和 .e0
这样的字符串作为数字传递。它还使用积极的后视来确保e
跟随数字。【参考方案5】:
对问题的一个简单(也许是简单化)的回答$x
numeric的内容如下:
if ($x eq $x+0) ....
它将原始$x
与转换为数值的$x
进行文本比较。
【讨论】:
如果你使用 "-w" 或 "use warnings;" 会抛出警告。 警告可以被删除$x eq (($x+0)."")
但是更糟糕的问题是在这个函数下,“1.0”不是数字
测试 $x+0 ne '' 就足够了。当您输入 0001 时,正确的数字将被检查为非数字。测试“.05”文本值时也是如此。【参考方案6】:
我不相信有任何内置的东西可以做到这一点。有关该主题的更多信息,请参阅Perlmonks on Detecting Numeric
【讨论】:
【参考方案7】:不完美,但您可以使用正则表达式:
sub isnumber
shift =~ /^-?\d+\.?\d*$/;
【讨论】:
与 andrewrk 的回答相同的问题:遗漏了许多甚至简单的案例,例如。 G。 '.7'【参考方案8】:可以在Regexp::Common 中找到更强大的正则表达式。
听起来你想知道 Perl 是否认为变量是数字的。这是一个捕获该警告的函数:
sub is_number
my $n = shift;
my $ret = 1;
$SIG"__WARN__" = sub $ret = 0;
eval my $x = $n + 1 ;
return $ret
另一种选择是在本地关闭警告:
no warnings "numeric"; # Ignore "isn't numeric" warning
... # Use a variable that might not be numeric
请注意,非数字变量将被静默转换为 0,这可能就是您想要的。
【讨论】:
【参考方案9】:rexep 不完美……这是:
use Try::Tiny;
sub is_numeric
my ($x) = @_;
my $numeric = 1;
try
use warnings FATAL => qw/numeric/;
0 + $x;
catch
$numeric = 0;
;
return $numeric;
【讨论】:
【参考方案10】:试试这个:
If (($x !~ /\D/) && ($x ne "")) ...
【讨论】:
【参考方案11】:我觉得这很有趣
if ( $value + 0 eq $value)
# A number
push @args, $value;
else
# A string
push @args, "'$value'";
【讨论】:
你需要解释一个更好的,你是说你觉得它很有趣,但它回答了操作吗?尝试解释为什么您的答案是所提问题的解决方案 例如我的 $value 是 1,$value + 0 保持不变 1。通过比较 $value 1 等于 1。如果 $value 是一个字符串说“swadhi”然后 $value + 0 变为字符串 "swadhi" + 0 = 其他数字的 ascii 值。【参考方案12】:我个人认为要走的路是依靠 Perl 的内部上下文来使解决方案防弹。一个好的正则表达式可以匹配所有有效的数值而不匹配任何非数值(反之亦然),但是由于有一种方法可以使用解释器正在使用的相同逻辑,因此直接依赖它应该更安全。
由于我倾向于使用-w
运行我的脚本,我不得不将“值加零”的结果与原始值进行比较的想法与@ysth 基于no warnings
的方法相结合:
do
no warnings "numeric";
if ($x + 0 ne $x) return "not numeric"; else return "numeric";
【讨论】:
【参考方案13】:您可以使用正则表达式来确定 $foo 是否为数字。
看这里: How do I determine whether a scalar is a number
【讨论】:
【参考方案14】:if ( 定义 $x && $x !~ m/\D/ ) 要么 $x = 0 如果 ! $x; if ( $x !~ m/\D/)
这与 Veekay 的回答略有不同,但让我解释一下我做出改变的理由。
对未定义的值执行正则表达式会导致错误溢出,并且会导致代码在许多(如果不是大多数)环境中退出。在运行表达式之前测试值是否已定义或像我在替代示例中所做的那样设置默认情况,至少会保存您的错误日志。
【讨论】:
以上是关于如何判断一个变量在 Perl 中是不是有数值?的主要内容,如果未能解决你的问题,请参考以下文章