89 votes

Comment savoir si une variable a une valeur numérique en Perl ?

Existe-t-il un moyen simple en Perl qui me permette de déterminer si une variable donnée est numérique ? Je ne sais pas si c'est le cas, mais c'est une bonne idée :

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

serait idéal. Une technique qui n'émettra pas d'avertissement lorsque le -w L'utilisation d'un commutateur est certainement préférable.

134voto

nohat Points 2818

Utilice Scalar::Util::looks_like_number() qui utilise la fonction looks_like_number() de l'API Perl C, ce qui est probablement la façon la plus efficace de procéder. Notez que les chaînes "inf" et "infinity" sont traitées comme des nombres.

Exemple :

#!/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";
}

Donne ce résultat :

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 aussi

1 votes

Et comme d'habitude avec les docs perl, il est difficile de trouver le fichier définition de ce que fait la fonction est assez difficile. Suivre la piste perldoc perlapi nous dit : Tester si le contenu d'une SV ressemble à un nombre (ou est un nombre). "Inf" et "Infinity" sont traités comme des nombres (et n'émettront donc pas d'avertissement non numérique), même si votre atof() ne les comprend pas. Une spécification difficilement testable...

3 votes

La description dans Scalar::Util est correcte, looks_like_number vous dit si votre entrée est quelque chose que Perl traiterait comme un nombre, ce qui n'est pas nécessairement la meilleure réponse à cette question. La mention de atof n'est pas pertinente, atof ne fait pas partie de CORE: : ou POSIX (vous devriez regarder strtod qui a subsumé atof et /est/ partie de POSIX) et supposer que ce que Perl pense être un nombre est une entrée numérique valide pour les fonctions C est évidemment très erroné.

0 votes

Très belle fonction :) pour undef et les chaînes non numériques renvoie 0, pour les chaînes numériques renvoie 1, pour les entiers renvoie 4352 et pour les flottants renvoie 8704 :) en général un nombre >0 est détecté. Je l'ai testé sous linux.

25voto

ysth Points 54757

La question initiale était de savoir si une variable était numérique, et non si elle "avait une valeur numérique".

Certains opérateurs ont des modes de fonctionnement distincts pour les opérandes numériques et les opérandes de type chaîne de caractères, le terme "numérique" désignant tout ce qui était à l'origine un nombre ou qui a été utilisé dans un contexte numérique (par exemple dans $x = "123"; 0+$x avant l'ajout, $x est une chaîne de caractères, il est ensuite considéré comme numérique).

Voici un moyen de le savoir :

if ( length( do { no warnings "numeric"; $x & "" } ) ) {
    print "$x is numeric\n";
}

Si la fonction "bitwise" est activée, cela signifie que & n'est qu'un opérateur numérique et ajoute une chaîne de caractères distincte &. vous devez le désactiver :

if ( length( do { no if $] >= 5.022, "feature", "bitwise"; no warnings "numeric"; $x & "" } ) ) {
    print "$x is numeric\n";
}

(bitwise est disponible dans perl 5.022 et plus, et activé par défaut si vous utilisez la fonction use 5.028; ou plus).

0 votes

Excellent, merci ! C'est exactement ce que je cherchais.

0 votes

Si je place votre routine dans un sub, j'obtiens un comportement étrange en ce sens qu'elle détecte correctement les valeurs non numériques, jusqu'à ce que j'essaie la première valeur numérique, qui est également détectée correctement comme vraie, mais ensuite, tout le reste à partir de là est également vrai. Quand je mets un eval autour de la partie length(...), cependant, cela fonctionne bien tout le temps. Une idée de ce que j'ai raté ? sub numeric { $obj = shift; no warnings "numeric"; return eval('length($obj & "")'); }

0 votes

@yogibimbi : vous réutilisez la même variable $obj à chaque fois ; essayez my $obj = shift; . Pourquoi l'évaluation ?

23voto

naumcho Points 2830

Consultez le module CPAN Regexp::Common . Je pense qu'il fait exactement ce dont vous avez besoin et qu'il gère tous les cas particuliers (par exemple, les nombres réels, la notation scientifique, etc.

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

11voto

superjoe30 Points 6876

En général, la validation des nombres se fait à l'aide d'expressions régulières. Ce code déterminera si quelque chose est numérique et vérifiera qu'il n'y a pas de variables indéfinies afin de ne pas générer d'avertissements :

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

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

En voici quelques-uns matériel de lecture que vous devriez consulter.

2 votes

Je pense que l'on s'éloigne un peu du sujet, en particulier lorsque l'auteur de la question a dit "simple". De nombreux cas, y compris la notation scientifique, sont loin d'être simples. A moins de l'utiliser pour un module, je ne m'inquiéterais pas de ces détails. Parfois, la simplicité est préférable. Ne mettez pas le sirop de chocolat dans la vache pour faire du lait au chocolat !

0 votes

'.7' est probablement l'un des cas les plus simples qui n'est toujours pas pris en compte... mieux vaut essayer /^[+-] ? \d * \. ? \d +$/ pour les flottants. Ma variante, qui prend également en compte la notation scientifique : /^[+-] ? \d * \. ? \d +(? :(?:e|E) \d +)?$/

0 votes

En \d*\.?\d+ la partie introduit un ReDoS risque. Je recommande /^[+-]?(?!\.(?!\d)|$)\d*(?:\.\d*)?$/ o /^[+-]?(?!\.(?!\d)|$)\d*(?:\.\d*)?(?:(?<=[\d.])e[+-]?\d+)?$/‌​i pour inclure la notation scientifique ( explication et exemples ). Cette méthode utilise une anticipation négative doublée afin d'éviter les chaînes de caractères telles que . y .e0 de passer pour des numéros. Il utilise également un lookbehind positif pour s'assurer que les e suit un numéro.

9voto

Peter Vanroose Points 51

Une réponse simple (et peut-être simpliste) à la question est le contenu de $x numérique est la suivante :

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

Il procède à une comparaison textuelle de l'original $x avec le $x converti en valeur numérique.

1 votes

Cela provoquera des avertissements si vous utilisez "-w" ou "use warnings ;".

1 votes

Les avertissements peuvent être supprimés $x eq (($x+0)."") Cependant, le problème le plus grave est que, dans cette fonction, "1.0" n'est pas numérique.

1 votes

Il suffit de tester $x+0 ne ''. Lorsque vous écrivez 0001, le nombre correct est considéré comme non numérique. il en va de même lorsque vous testez la valeur de texte '.05'.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X