Perlで変数に数値があるかどうかを確認するにはどうすればよいですか?


83

特定の変数が数値であるかどうかを判断できるPerlの簡単な方法はありますか?次のようなもの:

if (is_number($x))
{ ... }

理想的です。-wスイッチが使用されているときに警告をスローしない手法が確かに推奨されます。

回答:


128

Scalar::Util::looks_like_number()内部PerlC 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

参照:


1
そして、perl docsでいつものように、関数が何をするかについての実際の定義を見つけることはかなり難しいです。証跡をたどると、次のことperldoc perlapiがわかります。SVの内容が数字のように見える(または数字である)かどうかをテストします。「Inf」と「Infinity」は、atof()がそれらを取得しなくても、数値として扱われます(したがって、数値以外の警告は発行されません)。ほとんどテスト可能なスペック...

3
Scalar :: Utilの説明は問題ありません。looks_like_numberは、入力がPerlが数値として扱うものであるかどうかを示しますが、これは必ずしもこの質問に対する最良の答えではありません。atofの言及は無関係であり、atofはCORE ::またはPOSIXの一部ではありません(atofおよび/ is /がPOSIXの一部であるstrtodを確認する必要があります)、Perlが数値と見なすものが有効な数値入力であると想定しますC関数への変換は明らかに非常に間違っています。
MkV 2012年

非常に優れた関数:)undefおよび非数値文字列の場合は0を返し、数値文字列の場合は1を返し、整数の場合は4352を返し、浮動小数点数の場合は8704を返します:)通常> 0の数値が検出されます。Linuxでテストしました。
znik 2015年

1
私は一般的にこの関数が好きですが、大きなintを検討します。1000000は追跡するゼロがたくさんあり、エラーを懇願しますが、1,000,000は3要素配列と見なされるため、Perlは1_000_000を受け入れますが、looks_like_number()はノーと言います。私を悲しくさせる。
Dave Jacoby 2015

注:のような16進文字列0x12は、このテストでは数値と見なされません
Adam Katz

23

CPANモジュールRegexp :: Commonを確認してください。必要なことを正確に実行し、すべてのエッジケース(実数、科学的記数法など)を処理すると思います。例えば

use Regexp::Common;
if ($var =~ /$RE{num}{real}/) { print q{a number}; }

23

元々の質問は、変数が「数値を持っている」かどうかではなく、変数が数値であるかどうかをどのように判断するかでした。

数値オペランドと文字列オペランドに別々の操作モードを持つ演算子がいくつかあります。「数値」とは、元々数値であったか、数値コンテキストで使用されたことがあるものを意味します(たとえば$x = "123"; 0+$x、加算前は$x文字列であり、その後は文字列です。数値と見なされます)。

伝える1つの方法はこれです:

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

(bitwiseはperl 5.022以降で使用可能であり、use 5.028;それ以上の場合はデフォルトで有効になっています。)


優れたありがとう!これはまさに私が探していたものです。
Juan A. Navarro 2012年

ルーチンをサブにパッケージ化すると、最初の数値を試すまで、数値以外の値が正しく検出されるという奇妙な動作が発生します。これもtrueとして正しく検出されますが、それ以降はすべても真実です。ただし、length(...)の部分にevalを付けると、常に正常に機能します。私が欠けていたものは何ですか?sub numeric { $obj = shift; no warnings "numeric"; return eval('length($obj & "")'); }
yogibimbi 2013

@yogibimbi:毎回同じ$ obj変数を再利用しています。試してみてくださいmy $obj = shift;。なぜ評価?
ysth 2013

おっと、私の悪い、私は使用しましたmy $obj = shift、もちろん、それを私のコードからコメントに正しく転送しませんでした、私はそれを少し編集しました。ただし、sub numeric { my $obj = shift; no warnings "numeric"; return length($obj & ""); }同じエラーが発生します。もちろん、秘密のグローバル変数を持つことで動作が説明されます。それはまさにその場合に私が期待することですが、残念ながら、それはそれほど単純ではありません。また、それはstrict&によってキャッチされwarningsます。エラーを取り除くためにかなり必死の試みでevalを試しましたが、うまくいきました。より深い推論はなく、試行錯誤だけです。
yogibimbi 2013

それをチェックアウト:sub numeric { my $obj = shift; no warnings "numeric"; return length($obj & ""); } print numeric("w") . "\n"; #=>0print numeric("x") . "\n"; #=>0print numeric("1") . "\n"; #=>0print numeric(3) . "\n"; #=>1print numeric("w") . "\n"; #=>1。長さの周りにeval( '')を置くと、最後の出力は0になります。図に行きます。
yogibimbi 2013

9

質問に対する単純な(そしておそらく単純な)答えは、$x数値の内容は次のとおりです。

if ($x  eq  $x+0) { .... }

これは、元のテキストを比較し$x$x数値に変換します。


1
「-w」または「usewarnings;」を使用すると、警告がスローされます。
デレクパーク

1
警告は削除できます$x eq (($x+0)."")が、さらに悪い問題は、この関数では「1.0」が数値ではないことです
同名の2015年

1
$ x + 0 ne ''をテストするだけで十分です。0001にテキストメッセージを送信すると、正しい番号が非番号としてチェックされます。'.05'テキスト値をテストする場合も同じです。
znik 2015年

8

通常、数値の検証は正規表現を使用して行われます。このコードは、何かが数値であるかどうかを判別し、警告をスローしないように未定義の変数をチェックします。

sub is_integer {
   defined $_[0] && $_[0] =~ /^[+-]?\d+$/;
}

sub is_float {
   defined $_[0] && $_[0] =~ /^[+-]?\d+(\.\d+)?$/;
}

ここにいくつかあります あなたが見るべき読み物があります。


2
特にアスカーが/ simple /と言ったとき、これは少し逸脱すると思います。科学的記数法を含む多くの場合は、ほとんど単純ではありません。これをモジュールに使用しない限り、私はそのような詳細について心配することはありません。単純さが最善の場合もあります。チョコレートミルクを作るためにチョコレートシロップを牛に入れないでください!
osirisgothra 2014年

'.7'は、おそらくまだ見逃されている最も単純なケースの1つです... floatには/^[+-]?\d*\.?\d+$/を試してください。科学的記数法も考慮した私の変種:/ ^ [+
アコンカグア

この\d*\.?\d+部分は、ReDoSリスクをもたらします。私はお勧め/^[+-]?(?!\.(?!\d)|$)\d*(?:\.\d*)?$/または/^[+-]?(?!\.(?!\d)|$)\d*(?:\.\d*)?(?:(?<=[\d.])e[+-]?\d+)?$/i科学的表記法(含めるように説明と例を代わりに)。この用途倍増否定先読みはまたのような文字列を予防.し、.e0数値として渡すから。また、ポジティブルックビハインドを使用して、e次の数値を確認します。
Adam Katz 2016

3

完璧ではありませんが、正規表現を使用できます。

sub isnumber 
{
    shift =~ /^-?\d+\.?\d*$/;
}

andrewrkの答えと同じ問題:「。7」などの単純なケースでも多く見落とす
アコンカグア


2

もう少し堅牢な正規表現は、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に変換されることに注意してください。これは、おそらくとにかく必要なものです。


2

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

1

これを試して:

If (($x !~ /\D/) && ($x ne "")) { ... }

1

私はこれが面白いと思いました

if ( $value + 0 eq $value) {
    # A number
    push @args, $value;
} else {
    # A string
    push @args, "'$value'";
}

あなたはより良い説明をする必要があります、あなたはそれが面白いと思うと言っていますが、それはopに答えますか?あなたの答えが尋ねられた質問の解決策である理由を説明してみてください
Kumar Saurabh 2015年

たとえば、私の$ valueは1で、$ value +0は同じ1のままです。$ value1と比較すると1になります。$ valueが「swadhi」という文字列の場合、$ value +0は文字列「swadhi」のASCII値になります。 + 0 =その他の数値。
Swadhikar 2015年

0

個人的には、Perlの内部コンテキストに依存して、ソリューションを防弾にする方法だと思います。優れた正規表現は、すべての有効な数値に一致し、非数値には一致しない(またはその逆)可能性がありますが、インタープリターが使用しているのと同じロジックを使用する方法があるため、直接それに依存する方が安全です。

スクリプトを-wで実行する傾向があるため、「値プラスゼロ」の結果を元の値と比較するというアイデアをno warnings、@ ysthのベースのアプローチと組み合わせる必要がありました。

do { 
    no warnings "numeric";
    if ($x + 0 ne $x) { return "not numeric"; } else { return "numeric"; }
}


-1

if(defined $ x && $ x!〜m / \ D /){}または$ x = 0 if!$ x; if($ x!〜m / \ D /){}

これはVeekayの答えのわずかなバリエーションですが、変更の理由を説明させてください。

未定義の値に対して正規表現を実行すると、エラースピューが発生し、ほとんどの環境ではないにしても、多くの環境でコードが終了します。式を実行する前に、値が定義されているかどうかをテストするか、別の例で行ったようにデフォルトのケースを設定すると、少なくともエラーログが保存されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.