Utilisez Scalar::Util::looks_like_number()
qui utilise la fonction looks_like_number () de l'API Perl C interne, ce qui est probablement le moyen le plus efficace de le faire. Notez que les chaînes "inf" et "infinity" sont traitées comme des nombres.
Exemple:
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";
}
Donne cette sortie:
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
Voir également:
perldoc perlapi
nous dit: Teste si le contenu d'un SV ressemble à un nombre (ou est un nombre). "Inf" et "Infinity" sont traités comme des nombres (donc n'émettra pas d'avertissement non numérique), même si votre atof () ne les fait pas. À peine une spécification testable ...0x12
sont pas considérées comme des nombres par ce test.Consultez le module CPAN Regexp :: Common . Je pense qu'il fait exactement ce dont vous avez besoin et gère tous les cas extrêmes (par exemple les nombres réels, la notation scientifique, etc.). par exemple
use Regexp::Common; if ($var =~ /$RE{num}{real}/) { print q{a number}; }
la source
La question initiale était de savoir comment savoir si une variable était numérique, et non si elle "a une valeur numérique".
Il existe quelques opérateurs qui ont des modes de fonctionnement séparés pour les opérandes numériques et de chaîne, où "numérique" signifie tout ce qui était à l'origine un nombre ou qui a déjà été utilisé dans un contexte numérique (par exemple
$x = "123"; 0+$x
, avant l'addition, il$x
y a une chaîne, après est considéré comme numérique).Une façon de le dire est la suivante:
if ( length( do { no warnings "numeric"; $x & "" } ) ) { print "$x is numeric\n"; }
Si la fonctionnalité au niveau du bit est activée, cela crée
&
uniquement un opérateur numérique et ajoute un&.
opérateur de chaîne distinct , vous devez la désactiver:if ( length( do { no if $] >= 5.022, "feature", "bitwise"; no warnings "numeric"; $x & "" } ) ) { print "$x is numeric\n"; }
(au niveau du bit est disponible dans perl 5.022 et au-dessus, et activé par défaut si vous
use 5.028;
ou au-dessus.)la source
sub numeric { $obj = shift; no warnings "numeric"; return eval('length($obj & "")'); }
my $obj = shift;
. Pourquoi l'eval?my $obj = shift
, bien sûr, juste ne pas le transférer correctement de mon code au commentaire, je l'ai un peu édité. Cependant,sub numeric { my $obj = shift; no warnings "numeric"; return length($obj & ""); }
produit la même erreur. Bien sûr, avoir une variable globale clandestine expliquerait le comportement, c'est exactement ce à quoi je m'attendrais dans ce cas, mais malheureusement, ce n'est pas si simple. De plus, cela serait capturé parstrict
&warnings
. J'ai essayé l'évaluation dans une tentative plutôt désespérée de me débarrasser de l'erreur, et cela a fonctionné. Pas de raisonnement plus profond, juste des essais et des erreurs.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
. Si vous mettez un eval ('') autour de la longueur, la dernière impression donnerait un 0, comme il se doit. Allez comprendre.Une réponse simple (et peut-être simpliste) à la question est que le contenu de
$x
numérique est le suivant:if ($x eq $x+0) { .... }
Il effectue une comparaison textuelle de l'original
$x
avec le$x
converti en une valeur numérique.la source
$x eq (($x+0)."")
mais le pire problème est que sous cette fonction, "1.0" n'est pas numériqueLa validation des nombres est généralement effectuée avec des expressions régulières. Ce code déterminera si quelque chose est numérique et vérifiera les variables non définies pour ne pas lancer d'avertissements:
sub is_integer { defined $_[0] && $_[0] =~ /^[+-]?\d+$/; } sub is_float { defined $_[0] && $_[0] =~ /^[+-]?\d+(\.\d+)?$/; }
Voici quelques lectures que vous devriez consulter.
la source
\d*\.?\d+
partie introduit un risque ReDoS . Je recommande/^[+-]?(?!\.(?!\d)|$)\d*(?:\.\d*)?$/
ou/^[+-]?(?!\.(?!\d)|$)\d*(?:\.\d*)?(?:(?<=[\d.])e[+-]?\d+)?$/i
d'inclure la notation scientifique ( explication et exemples ) à la place. Cela utilise une anticipation négative doublée pour empêcher également les chaînes comme.
et.e0
de passer sous forme de nombres. Il utilise également un regard positif en arrière pour s'assurer que lee
suit un nombre.Pas parfait, mais vous pouvez utiliser une regex:
sub isnumber { shift =~ /^-?\d+\.?\d*$/; }
la source
Je ne pense pas qu'il y ait quoi que ce soit pour le faire. Pour en savoir plus sur le sujet, consultez Perlmonks on Detecting Numeric
la source
Une regex légèrement plus robuste peut être trouvée dans Regexp :: Common .
Il semble que vous vouliez savoir si Perl pense qu'une variable est numérique. Voici une fonction qui intercepte cet avertissement:
sub is_number{ my $n = shift; my $ret = 1; $SIG{"__WARN__"} = sub {$ret = 0}; eval { my $x = $n + 1 }; return $ret }
Une autre option consiste à désactiver l'avertissement localement:
{ no warnings "numeric"; # Ignore "isn't numeric" warning ... # Use a variable that might not be numeric }
Notez que les variables non numériques seront silencieusement converties en 0, ce qui est probablement ce que vous vouliez de toute façon.
la source
rexep pas parfait ... c'est:
use Try::Tiny; sub is_numeric { my ($x) = @_; my $numeric = 1; try { use warnings FATAL => qw/numeric/; 0 + $x; } catch { $numeric = 0; }; return $numeric; }
la source
Essaye ça:
If (($x !~ /\D/) && ($x ne "")) { ... }
la source
J'ai trouvé ça intéressant
if ( $value + 0 eq $value) { # A number push @args, $value; } else { # A string push @args, "'$value'"; }
la source
Personnellement, je pense que la voie à suivre est de s'appuyer sur le contexte interne de Perl pour rendre la solution à toute épreuve. Une bonne expression rationnelle pourrait correspondre à toutes les valeurs numériques valides et aucune des valeurs non numériques (ou vice versa), mais comme il existe un moyen d'utiliser la même logique que l'interpréteur utilise, il devrait être plus sûr de s'appuyer directement sur cela.
Comme j'ai tendance à exécuter mes scripts avec
-w
, j'ai dû combiner l'idée de comparer le résultat de "valeur plus zéro" à la valeur d'origine avec l'no warnings
approche basée sur @ysth:do { no warnings "numeric"; if ($x + 0 ne $x) { return "not numeric"; } else { return "numeric"; } }
la source
Vous pouvez utiliser des expressions régulières pour déterminer si $ foo est un nombre (ou non).
Jetez un œil ici: Comment déterminer si un scalaire est un nombre
la source
if (défini $ x && $ x! ~ m / \ D /) {} ou $ x = 0 if! $ x; si ($ x! ~ m / \ D /) {}
Il s'agit d'une légère variation de la réponse de Veekay, mais laissez-moi vous expliquer mon raisonnement pour le changement.
L'exécution d'une expression régulière sur une valeur indéfinie provoquera un spew d'erreur et entraînera la fermeture du code dans de nombreux, sinon la plupart des environnements. Tester si la valeur est définie ou définir un cas par défaut comme je l'ai fait dans l'exemple alternatif avant d'exécuter l'expression enregistrera au minimum votre journal des erreurs.
la source