Validez qu'une chaîne est un entier positif


200

Je voudrais que le test de sécurité le plus simple vérifie qu'une chaîne en JavaScript est un entier positif.

isNaN(str)renvoie true pour toutes sortes de valeurs non entières et parseInt(str)renvoie des entiers pour les chaînes flottantes, comme "2.5". Et je ne veux pas non plus avoir à utiliser un plugin jQuery.


7
Autorisez-vous "+2"? Que diriez-vous de "0.1e1"? Que diriez-vous de "1.0000"?
Phrogz

La isNaN()fonction se comporte comme elle le fait parce que le concept Not a Numbera une signification très spécifique dans la spécification en virgule flottante IEEE 794. Il n'a pas l'intention de répondre à la simple question familière: "cette valeur n'est-elle pas un nombre?"
Pointy

3
La question est vague à l'extrême. Vous ne pouvez pas valider qu '"une chaîne est un entier", car il n'y a rien de tel - aucun objet ne peut être à la fois une chaîne et un nombre. Vous vouliez probablement dire "comment tester si une chaîne est une représentation valide d'un entier", mais pour répondre à cela, nous devons savoir de quelle langue ou "culture" vous parlez. Par exemple, ٢‎٣٤ou MCMXIXsont tous deux des représentations entières valides, mais je ne pense pas que vous recherchiez un code capable de les analyser. Pourriez-vous spécifier les formats numériques que vous allez prendre en charge, car les gens semblent confus à ce sujet.
georg

5
Je pense que «vague à l'extrême» est en soi un peu extrême; mais de toute façon ... Le contexte est la validation d'un champ de saisie de quantité sur un panier utilisé dans un pays anglophone, donc uniquement des chiffres occidentaux. Par "positif", je voulais dire plus grand que zéro. Accepterais-je ce qui suit: "+2" oui, "0.1e1" non, "1.000" bien pourquoi pas. Cependant, si vous pouvez fournir une réponse qui peut être ajustée pour inclure / exclure ces divers scénarios spécialisés, je promets de vous donner des avantages supplémentaires (et je suis sûr que d'autres le feront aussi).
Mick Byrne

Réponses:


296

Deux réponses pour vous:

  • Basé sur l'analyse

  • Expression régulière

Notez que dans les deux cas, j'ai interprété "entier positif" comme incluant 0, même s'il 0n'est pas positif. J'inclus des notes si vous voulez interdire 0.

Basé sur l'analyse

Si vous voulez que ce soit une chaîne entière décimale normalisée sur une plage raisonnable de valeurs, vous pouvez le faire:

function isNormalInteger(str) {
    var n = Math.floor(Number(str));
    return n !== Infinity && String(n) === str && n >= 0;
}

ou si vous souhaitez autoriser les espaces et les zéros non significatifs:

function isNormalInteger(str) {
    str = str.trim();
    if (!str) {
        return false;
    }
    str = str.replace(/^0+/, "") || "0";
    var n = Math.floor(Number(str));
    return n !== Infinity && String(n) === str && n >= 0;
}

Banc d'essai en direct (sans gérer les zéros ou les espaces):

Banc d'essai en direct ( avec gestion des zéros et des espaces):

Si vous souhaitez refuser 0, passez simplement >= 0à > 0. (Ou, dans la version qui autorise les zéros non significatifs, supprimez le || "0"sur la replaceligne.)

Comment ça marche:

  1. Dans la version autorisant les espaces et les zéros non significatifs:

    • str = str.trim(); supprime tout espace blanc de début et de fin.
    • if (!str) attrape une chaîne vide et retourne, inutile de faire le reste du travail.
    • str = str.replace(/^0+/, "") || "0"; supprime tous les 0 de tête de la chaîne - mais si cela se traduit par une chaîne vide, restaure un seul 0.
  2. Number(str): Convertir stren un nombre; le nombre peut bien avoir une partie fractionnaire, ou peut l'être NaN.

  3. Math.floor: Tronque le nombre (coupe toute portion fractionnée).

  4. String(...): Reconvertit le résultat en une chaîne décimale normale. Pour les très gros nombres, cela ira à la notation scientifique, ce qui peut briser cette approche. (Je ne sais pas exactement où se trouve la division, les détails sont dans la spécification , mais pour les nombres entiers, je pense que c'est au point où vous avez dépassé 21 chiffres [date à laquelle le nombre est devenu très imprécis, comme IEEE-754 les nombres à double précision ont approximativement 15 chiffres de précision ..)

  5. ... === str: Compare cela à la chaîne d'origine.

  6. n >= 0: Vérifiez que c'est positif.

Notez que cela échoue pour l'entrée "+1", toute entrée en notation scientifique qui ne redevient pas la même notation scientifique à l' String(...)étape, et pour toute valeur que le type de nombre JavaScript utilise (virgule flottante binaire double précision IEEE-754) ne peut pas représenter avec précision quelle analyse est plus proche d'une valeur différente de celle donnée (qui comprend de nombreux entiers supérieurs à 9 007 199 254 740 992; par exemple, 1234567890123456789échouera). Le premier est une solution facile, les deux derniers moins.

Expression régulière

L'autre approche consiste à tester les caractères de la chaîne via une expression régulière, si votre objectif est de permettre (disons) simplement une option +suivie de l'une 0ou de l' autre ou d'une chaîne au format décimal normal:

function isNormalInteger(str) {
    return /^\+?(0|[1-9]\d*)$/.test(str);
}

Banc d'essai en direct:

Comment ça marche:

  1. ^: Correspond au début de la chaîne

  2. \+?: Autoriser une seule option +(supprimez-la si vous ne le souhaitez pas)

  3. (?:...|...): Autorisez l'une de ces deux options (sans créer de groupe de capture):

    1. (0|...): Autoriser 0seul ...

    2. (...|[1-9]\d*): ... ou un nombre commençant par autre chose que 0et suivi d'un nombre quelconque de chiffres décimaux.

  4. $: Correspond à la fin de la chaîne.

Si vous voulez interdire 0(car ce n'est pas positif), l'expression régulière devient juste /^\+?[1-9]\d*$/(par exemple, nous pouvons perdre l'alternance que nous devions autoriser 0).

Si vous souhaitez autoriser les zéros non significatifs (0123, 00524), remplacez simplement l'alternance (?:0|[1-9]\d*)par\d+

function isNormalInteger(str) {
    return /^\+?\d+$/.test(str);
}

Si vous souhaitez autoriser les espaces, ajoutez \s*juste après ^et \s*juste avant $.

Remarque lorsque vous convertissez cela en nombre: sur les moteurs modernes, il serait probablement bien de l'utiliser +strou Number(str)de le faire, mais les anciens peuvent étendre ceux-ci d'une manière non standard (mais autrefois autorisée) qui dit qu'un zéro de tête signifie octal (base 8), par exemple "010" => 8. Une fois que vous avez validé le nombre, vous pouvez l'utiliser en toute sécurité parseInt(str, 10)pour vous assurer qu'il est analysé en décimal (base 10). parseIntignorerait les ordures à la fin de la chaîne, mais nous nous sommes assurés qu'il n'y en a pas avec l'expression régulière.


both Number(' ')et Number('')return 0while devraient revenir à la NaNplace.
neurino

@TJCrowder ok mais j'ai besoin que des chaînes comme '' 2a '' soient rejetées d'une manière ou d'une autre, parseIntretourne 2.
neurino

@neurino: /\D/.test('2a')est vrai (car il n'y a pas de chiffre). Alors peut-être if (!/\D/.test(str) && !isNan((num = parseInt(str, 10))) { /* Valid number in num */}... Juste au dessus de ma tête ...
TJ Crowder

En termes de performances, quelle méthode serait plus rapide? Ou lequel est conseillé?
phkavitha

@phkavitha: La réponse aux questions de performances JavaScript est presque toujours "Cela dépend" car les différents moteurs sont tellement différents les uns des autres. Vous pouvez tester vos moteurs / navigateurs cibles en utilisant jsperf.com . Je ne pense pas que cette opération soit susceptible d'être le goulot d'étranglement dans une application donnée ... :-)
TJ Crowder

76

Solution 1

Si nous considérons un entier JavaScript comme une valeur maximale 4294967295(c'est-à-dire Math.pow(2,32)-1), alors la solution courte suivante fonctionnera parfaitement:

function isPositiveInteger(n) {
    return n >>> 0 === parseFloat(n);
}

LA DESCRIPTION:

  1. L'opérateur de décalage à droite de remplissage nul fait trois choses importantes:
    • tronque la partie décimale
      • 123.45 >>> 0 === 123
    • fait le changement pour les nombres négatifs
      • -1 >>> 0 === 4294967295
    • "fonctionne" dans la gamme de MAX_INT
      • 1e10 >>> 0 === 1410065408
      • 1e7 >>> 0 === 10000000
  2. parseFloatcorrige l'analyse des numéros de chaîne (paramètre NaNpour les chaînes non numériques)

TESTS:

"0"                     : true
"23"                    : true
"-10"                   : false
"10.30"                 : false
"-40.1"                 : false
"string"                : false
"1234567890"            : true
"129000098131766699.1"  : false
"-1e7"                  : false
"1e7"                   : true
"1e10"                  : false
"1edf"                  : false
" "                     : false
""                      : false

DÉMO: http://jsfiddle.net/5UCy4/37/


Solution 2

Une autre méthode est valable pour toutes les valeurs numériques valables jusqu'à Number.MAX_VALUE, c'est-à-dire jusqu'à environ 1.7976931348623157e+308:

function isPositiveInteger(n) {
    return 0 === n % (!isNaN(parseFloat(n)) && 0 <= ~~n);
}

LA DESCRIPTION:

  1. !isNaN(parseFloat(n))est utilisé pour filtrer pures valeurs de chaîne, par exemple "", " ", "string";
  2. 0 <= ~~nfiltre négatif et de grandes valeurs non entières, par exemple "-40.1", "129000098131766699";
  3. (!isNaN(parseFloat(n)) && 0 <= ~~n)renvoie truesi la valeur est à la fois numérique et positive ;
  4. 0 === n % (...)vérifie si la valeur n'est pas flottante - ici (...)(voir 3) est évaluée comme 0dans le cas de falseet comme 1dans le cas de true.

TESTS:

"0"                     : true
"23"                    : true
"-10"                   : false
"10.30"                 : false
"-40.1"                 : false
"string"                : false
"1234567890"            : true
"129000098131766699.1"  : false
"-1e10"                 : false
"1e10"                  : true
"1edf"                  : false
" "                     : false
""                      : false

DÉMO: http://jsfiddle.net/5UCy4/14/


La version précédente:

function isPositiveInteger(n) {
    return n == "0" || ((n | 0) > 0 && n % 1 == 0);
}

DÉMO: http://jsfiddle.net/5UCy4/2/


Essayez une chaîne vide ou un grand nombre tel que 1290000981231123.1 - jsfiddle.net/5UCy4/1
Niko

@gdoron Comme pour ~~valcouper une partie fractionnaire. Il est un peu plus rapide car effectue une opération au niveau du bit au lieu de deux.
VisioN

Très bonne réponse. Personnellement, je préférerais être un peu plus bavard pour plus de lisibilité(!isNaN(parseFloat(n)) && n % 1 === 0 && 0 <= ~~n)
Ramy Nasr

true, false, 0xFFet d'autres valeurs réussissent isPositiveIntegersans être détectées.
Xeoncross

1
Celui-ci gère même les zéros rembourrés (bon!). Vous pouvez ajouter cela à vos tests.
Rashack

21

On dirait qu'une expression régulière est la voie à suivre:

var isInt = /^\+?\d+$/.test('the string');

Fermez, sauf si "1.0" ou ".1e1" sont autorisés.
Phrogz

Eh bien 1290000192379182379123782900192981231 est un entier mais il n'est pas représentable exactement en nombres natifs JavaScript, donc avec une expression régulière, il est toujours nécessaire de faire une conversion numérique et de vérifier que cela fonctionne.
Pointy

Il s'agit d'une solution très imprécise.
usr

@usr: Je suppose que cela permet de diriger 0là où vous ne vous attendez pas normalement, mais pour la plupart, cela semble bien.
TJ Crowder

Il ne vérifie pas que la plage est au plus 2 ^ 31-1 et il n'autorise pas les chiffres arabes. C'est un hack, pas une bonne solution. La meilleure solution a le plus de votes positifs dans cette question. Pourquoi ne pas l'utiliser? C'est mieux à tous égards.
usr

17

La solution moderne qui fonctionne en nœud et sur plus de 90% de tous les navigateurs (sauf IE et Opera Mini) consiste à utiliser Number.isInteger suivi d'une simple vérification positive.

Number.isInteger(x) && x > 0

Cela a été finalisé dans ECMAScript 2015 .

function isPositiveInteger(x) {
    return Number.isInteger(x) && x > 0
}

Le Polyfil c'est:

Number.isInteger = Number.isInteger || function(value) {
  return typeof value === 'number' && 
    isFinite(value) && 
    Math.floor(value) === value;
};

Si vous devez prendre en charge des entrées qui peuvent être sous forme de chaîne ou de nombre , vous pouvez utiliser cette fonction contre laquelle j'ai écrit une grande suite de tests après que toutes les réponses existantes (01/02/2018) ont échoué sur une forme d'entrée.

function isPositiveInteger(v) {
  var i;
  return v && (i = parseInt(v)) && i > 0 && (i === v || ''+i === v);
}

4

C'est presque une question en double pour celle-ci:

Valider les nombres décimaux en JavaScript - IsNumeric ()

Sa réponse est:

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

ainsi, un entier positif serait:

function isPositiveInteger(n) {
  var floatN = parseFloat(n);
  return !isNaN(floatN) && isFinite(n) && floatN > 0
      && floatN % 1 == 0;
}

Je sais que c'est presque un doublon à cette question `` Valider les nombres en JavaScript '', j'ai lu tout cela, mais j'ai pensé qu'une question spécifique sur les représentations de chaînes d'entiers méritait sa propre page.
Mick Byrne

2
return ((parseInt(str, 10).toString() == str) && str.indexOf('-') === -1);

ne fonctionnera pas si vous donnez une chaîne comme «0001»


2

Ma fonction vérifie si le nombre est + ve et peut également avoir une valeur décimale.

       function validateNumeric(numValue){
            var value = parseFloat(numValue);
            if (!numValue.toString().match(/^[-]?\d*\.?\d*$/)) 
                    return false;
            else if (numValue < 0) {
                return false;
            }
            return true;        
        }

2

Juste pour construire sur la réponse de VisioN ci-dessus, si vous utilisez le plugin de validation jQuery, vous pouvez utiliser ceci:

$(document).ready(function() {
    $.validator.addMethod('integer', function(value, element, param) {
        return (value >>> 0 === parseFloat(value) && value > 0);
    }, 'Please enter a non zero integer value!');
}

Ensuite, vous pouvez utiliser dans votre ensemble de règles normales ou l'ajouter dynamiquement de cette façon:

$("#positiveIntegerField").rules("add", {required:true, integer:true});

2

Facile

function isInteger(num) {
  return (num ^ 0) === num;
}

console.log(isInteger(1));

Vous pouvez également étendre Number et lui attribuer la fonction via un prototype.


1

Si vous utilisez des formulaires HTML5, vous pouvez utiliser l'attribut min="0"pour l'élément de formulaire <input type="number" />. Ceci est pris en charge par tous les principaux navigateurs. Il n'implique pas Javascript pour des tâches aussi simples, mais est intégré dans le nouveau standard html. Il est documenté sur https://www.w3schools.com/tags/att_input_min.asp


0

(~~a == a)aest la chaîne.


1
Cela échouera pour les nombres négatifs: ~~"-1" == "-1"reviendra true. Et OP n'a demandé que des nombres entiers positifs.
Igor Milla

1
Échoue également pour «» et «».
neverfox

0

ES6:

Number.isInteger(Number(theNumberString)) > 0
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.