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


Quel est le moyen le plus propre et le plus efficace de valider les nombres décimaux en JavaScript?

Points bonus pour:

  1. Clarté. La solution devrait être propre et simple.
  2. Cross-plateforme.

Cas de test:

01. IsNumeric('-1')      => true
02. IsNumeric('-1.5')    => true
03. IsNumeric('0')       => true
04. IsNumeric('0.42')    => true
05. IsNumeric('.42')     => true
06. IsNumeric('99,999')  => false
07. IsNumeric('0x89f')   => false
08. IsNumeric('#abcdef') => false
09. IsNumeric('1.2.3')   => false
10. IsNumeric('')        => false
11. IsNumeric('blah')    => false

2148


origine


Réponses:


@ La réponse de Joel est assez proche, mais il échouera dans les cas suivants:

// Whitespace strings:
IsNumeric(' ')    == true;
IsNumeric('\t\t') == true;
IsNumeric('\n\r') == true;

// Number literals:
IsNumeric(-1)  == false;
IsNumeric(0)   == false;
IsNumeric(1.1) == false;
IsNumeric(8e5) == false;

Il y a quelque temps, j'ai dû mettre en œuvre un IsNumeric fonction, pour savoir si une variable contient une valeur numérique, quel que soit son type, ça pourrait être un String contenant une valeur numérique (je devais considérer aussi la notation exponentielle, etc.), un Number objet, pratiquement tout pourrait être passé à cette fonction, je ne pouvais pas faire des hypothèses de type, en prenant soin de la coercition de type (par exemple. +true == 1; mais true ne devrait pas être considéré comme "numeric").

Je pense qu'il vaut la peine de partager cet ensemble de +30 tests unitaires faite à de nombreuses implémentations de fonctions, et partage également celle qui passe tous mes tests:

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

P.S.  IsNaN & isFinite avoir un comportement confus dû à la conversion forcée en nombre. En ES6, Number.isNaN & Number.isFinite réglerait ces problèmes. Gardez cela à l'esprit lorsque vous les utilisez.


Mettre à jour : Voici comment jQuery le fait maintenant (2.2-stable):

isNumeric: function(obj) {
    var realStringObj = obj && obj.toString();
    return !jQuery.isArray(obj) && (realStringObj - parseFloat(realStringObj) + 1) >= 0;
}

Mettre à jour : Angulaire 4.3:

export function isNumeric(value: any): boolean {
    return !isNaN(value - parseFloat(value));
}

2756



Arrrgh! N'écoutez pas les réponses d'expressions régulières. RegEx est icky pour cela, et je ne parle pas seulement de la performance. Il est si facile de faire des erreurs subtiles, impossibles à repérer avec votre expression régulière.

Si vous ne pouvez pas utiliser isNaN(), cela devrait fonctionner beaucoup mieux:

function IsNumeric(input)
{
    return (input - 0) == input && (''+input).trim().length > 0;
}

Voici comment cela fonctionne:

le (input - 0) l'expression force JavaScript à faire une coercition de type sur votre valeur d'entrée; il doit d'abord être interprété comme un nombre pour l'opération de soustraction. Si cette conversion en nombre échoue, l'expression entraînera NaN. Ce numérique Le résultat est ensuite comparé à la valeur d'origine que vous avez transmise. Puisque le côté gauche est maintenant numérique, la coercition de type est à nouveau utilisée. Maintenant que l'entrée des deux côtés a été contrainte au même type à partir de la même valeur d'origine, vous pourriez penser qu'ils devraient toujours être les mêmes (toujours vrai). Cependant, il y a une règle spéciale qui dit NaN n'est jamais égal à NaN, et donc une valeur qui ne peut pas être convertie en un nombre (et seulement les valeurs qui ne peuvent pas être converties en nombres) donnera faux.

La vérification de la longueur est pour un cas particulier impliquant des chaînes vides. Notez également qu'il tombe sur votre test 0x89f, mais c'est parce que dans de nombreux environnements, c'est une bonne façon de définir un littéral numérique. Si vous voulez attraper ce scénario spécifique, vous pouvez ajouter une vérification supplémentaire. Encore mieux, si c'est votre raison de ne pas utiliser isNaN() alors juste envelopper votre propre fonction autour isNaN() cela peut aussi faire le contrôle supplémentaire.

En résumé, Si vous voulez savoir si une valeur peut être convertie en nombre, essayez de la convertir en nombre.


Je suis retourné et j'ai fait des recherches pour Pourquoi une chaîne d'espaces n'a pas eu la sortie attendue, et je pense que je l'ai maintenant: une chaîne vide est forcée à 0 plutôt que NaN. Simplement couper la chaîne avant que la vérification de la longueur gérera ce cas.

Exécuter les tests unitaires sur le nouveau code et cela échoue seulement sur les littéraux infini et booléen, et le seul moment qui devrait poser problème est de savoir si vous générez du code (qui saisira un littéral et vérifiera s'il est numérique? Vous devriez connaître), et ce serait un code étrange à générer.

Mais, La seule raison pour utiliser ceci est si pour une raison quelconque vous devez éviter isNaN ().


314



Cette façon semble bien fonctionner:

function IsNumeric(input){
    var RE = /^-{0,1}\d*\.{0,1}\d+$/;
    return (RE.test(input));
}

Et pour le tester:

// alert(TestIsNumeric());

function TestIsNumeric(){
    var results = ''
    results += (IsNumeric('-1')?"Pass":"Fail") + ": IsNumeric('-1') => true\n";
    results += (IsNumeric('-1.5')?"Pass":"Fail") + ": IsNumeric('-1.5') => true\n";
    results += (IsNumeric('0')?"Pass":"Fail") + ": IsNumeric('0') => true\n";
    results += (IsNumeric('0.42')?"Pass":"Fail") + ": IsNumeric('0.42') => true\n";
    results += (IsNumeric('.42')?"Pass":"Fail") + ": IsNumeric('.42') => true\n";
    results += (!IsNumeric('99,999')?"Pass":"Fail") + ": IsNumeric('99,999') => false\n";
    results += (!IsNumeric('0x89f')?"Pass":"Fail") + ": IsNumeric('0x89f') => false\n";
    results += (!IsNumeric('#abcdef')?"Pass":"Fail") + ": IsNumeric('#abcdef') => false\n";
    results += (!IsNumeric('1.2.3')?"Pass":"Fail") + ": IsNumeric('1.2.3') => false\n";
    results += (!IsNumeric('')?"Pass":"Fail") + ": IsNumeric('') => false\n";
    results += (!IsNumeric('blah')?"Pass":"Fail") + ": IsNumeric('blah') => false\n";

    return results;
}

J'ai emprunté ce regex de http://www.codetoad.com/javascript/isnumeric.asp. Explication:

/^ match beginning of string
-{0,1} optional negative sign
\d* optional digits
\.{0,1} optional decimal point
\d+ at least one digit
$/ match end of string

58



Yahoo! UI utilise ceci:

isNumber: function(o) {
    return typeof o === 'number' && isFinite(o);
}

45



function IsNumeric(num) {
     return (num >=0 || num < 0);
}

Cela fonctionne aussi pour les numéros de type 0x23.


44



La réponse acceptée a échoué votre test n ° 7 et je suppose que c'est parce que vous avez changé d'avis. Donc, ceci est une réponse à la réponse acceptée, avec laquelle j'ai eu des problèmes.

Au cours de certains projets, j'ai eu besoin de valider certaines données et d'être aussi certain que possible que c'est une valeur numérique javascript qui peut être utilisée dans les opérations mathématiques.

jQuery, et d'autres bibliothèques javascript incluent déjà une telle fonction, généralement appelée isNumeric. Il y a aussi poster sur stackoverflow cela a été largement accepté comme la réponse, la même routine générale que les bibliothèques mentionnées ci-dessus utilisent.

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

Tout d'abord, le code ci-dessus retournera vrai si l'argument était un tableau de longueur 1, et que cet élément unique était d'un type considéré comme numérique par la logique ci-dessus. À mon avis, si c'est un tableau, ce n'est pas numérique.

Pour atténuer ce problème, j'ai ajouté une vérification pour actualiser les tableaux de la logique

function isNumber(n) {
  return Object.prototype.toString.call(n) !== '[object Array]' &&!isNaN(parseFloat(n)) && isFinite(n);
}

Bien sûr, vous pouvez également utiliser Array.isArray, jquery $.isArray ou prototype Object.isArray au lieu de Object.prototype.toString.call(n) !== '[object Array]'

Mon deuxième problème était que les chaînes littérales entières hexadécimales négatives ("-0xA" -> -10) n'étaient pas comptées comme numériques. Cependant, les chaînes littérales d'entiers hexadécimaux positifs ("0xA" -> 10) ont été traitées comme numériques. J'avais besoin des deux pour être numérique valide.

J'ai ensuite modifié la logique pour en tenir compte.

function isNumber(n) {
  return Object.prototype.toString.call(n) !== '[object Array]' &&!isNaN(parseFloat(n)) && isFinite(n.toString().replace(/^-/, ''));
}

Si vous vous inquiétez de la création de l'expression régulière à chaque fois que la fonction est appelée, vous pouvez la réécrire dans une fermeture, quelque chose comme ça

var isNumber = (function () {
  var rx = /^-/;

  return function (n) {
      return Object.prototype.toString.call(n) !== '[object Array]' &&!isNaN(parseFloat(n)) && isFinite(n.toString().replace(rx, ''));
  };
}());

J'ai ensuite pris des CMS +30 cas de test et cloné le test sur jsfiddle ajouté mes cas de test supplémentaires et ma solution décrite ci-dessus.

Il ne peut pas remplacer la réponse largement acceptée / utilisée, mais si c'est plus de ce que vous attendez des résultats de votre fonction isNumeric alors j'espère que cela sera d'une certaine aide.

MODIFIER: Comme indiqué par Bergi, il y a d'autres objets possibles qui pourraient être considérés comme numériques et il serait préférable de mettre sur liste blanche que sur liste noire. Dans cet esprit, j'ajouterais aux critères.

Je souhaite que ma fonction isNumeric considère uniquement les nombres ou les chaînes

Dans cet esprit, il serait préférable d'utiliser

function isNumber(n) {
  return (Object.prototype.toString.call(n) === '[object Number]' || Object.prototype.toString.call(n) === '[object String]') &&!isNaN(parseFloat(n)) && isFinite(n.toString().replace(/^-/, ''));
}

Testez les solutions

var testHelper = function() {

  var testSuite = function() {
    test("Integer Literals", function() {
      ok(isNumber("-10"), "Negative integer string");
      ok(isNumber("0"), "Zero string");
      ok(isNumber("5"), "Positive integer string");
      ok(isNumber(-16), "Negative integer number");
      ok(isNumber(0), "Zero integer number");
      ok(isNumber(32), "Positive integer number");
      ok(isNumber("040"), "Octal integer literal string");
      ok(isNumber(0144), "Octal integer literal");
      ok(isNumber("-040"), "Negative Octal integer literal string");
      ok(isNumber(-0144), "Negative Octal integer literal");
      ok(isNumber("0xFF"), "Hexadecimal integer literal string");
      ok(isNumber(0xFFF), "Hexadecimal integer literal");
      ok(isNumber("-0xFF"), "Negative Hexadecimal integer literal string");
      ok(isNumber(-0xFFF), "Negative Hexadecimal integer literal");
    });

    test("Foating-Point Literals", function() {
      ok(isNumber("-1.6"), "Negative floating point string");
      ok(isNumber("4.536"), "Positive floating point string");
      ok(isNumber(-2.6), "Negative floating point number");
      ok(isNumber(3.1415), "Positive floating point number");
      ok(isNumber(8e5), "Exponential notation");
      ok(isNumber("123e-2"), "Exponential notation string");
    });

    test("Non-Numeric values", function() {
      equals(isNumber(""), false, "Empty string");
      equals(isNumber("        "), false, "Whitespace characters string");
      equals(isNumber("\t\t"), false, "Tab characters string");
      equals(isNumber("abcdefghijklm1234567890"), false, "Alphanumeric character string");
      equals(isNumber("xabcdefx"), false, "Non-numeric character string");
      equals(isNumber(true), false, "Boolean true literal");
      equals(isNumber(false), false, "Boolean false literal");
      equals(isNumber("bcfed5.2"), false, "Number with preceding non-numeric characters");
      equals(isNumber("7.2acdgs"), false, "Number with trailling non-numeric characters");
      equals(isNumber(undefined), false, "Undefined value");
      equals(isNumber(null), false, "Null value");
      equals(isNumber(NaN), false, "NaN value");
      equals(isNumber(Infinity), false, "Infinity primitive");
      equals(isNumber(Number.POSITIVE_INFINITY), false, "Positive Infinity");
      equals(isNumber(Number.NEGATIVE_INFINITY), false, "Negative Infinity");
      equals(isNumber(new Date(2009, 1, 1)), false, "Date object");
      equals(isNumber(new Object()), false, "Empty object");
      equals(isNumber(function() {}), false, "Instance of a function");
      equals(isNumber([]), false, "Empty Array");
      equals(isNumber(["-10"]), false, "Array Negative integer string");
      equals(isNumber(["0"]), false, "Array Zero string");
      equals(isNumber(["5"]), false, "Array Positive integer string");
      equals(isNumber([-16]), false, "Array Negative integer number");
      equals(isNumber([0]), false, "Array Zero integer number");
      equals(isNumber([32]), false, "Array Positive integer number");
      equals(isNumber(["040"]), false, "Array Octal integer literal string");
      equals(isNumber([0144]), false, "Array Octal integer literal");
      equals(isNumber(["-040"]), false, "Array Negative Octal integer literal string");
      equals(isNumber([-0144]), false, "Array Negative Octal integer literal");
      equals(isNumber(["0xFF"]), false, "Array Hexadecimal integer literal string");
      equals(isNumber([0xFFF]), false, "Array Hexadecimal integer literal");
      equals(isNumber(["-0xFF"]), false, "Array Negative Hexadecimal integer literal string");
      equals(isNumber([-0xFFF]), false, "Array Negative Hexadecimal integer literal");
      equals(isNumber([1, 2]), false, "Array with more than 1 Positive interger number");
      equals(isNumber([-1, -2]), false, "Array with more than 1 Negative interger number");
    });
  }

  var functionsToTest = [

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

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

    function(n) {
      return !isNaN((n));
    },

    function(n) {
      return !isNaN(parseFloat(n));
    },

    function(n) {
      return typeof(n) != "boolean" && !isNaN(n);
    },

    function(n) {
      return parseFloat(n) === Number(n);
    },

    function(n) {
      return parseInt(n) === Number(n);
    },

    function(n) {
      return !isNaN(Number(String(n)));
    },

    function(n) {
      return !isNaN(+('' + n));
    },

    function(n) {
      return (+n) == n;
    },

    function(n) {
      return n && /^-?\d+(\.\d+)?$/.test(n + '');
    },

    function(n) {
      return isFinite(Number(String(n)));
    },

    function(n) {
      return isFinite(String(n));
    },

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

    function(n) {
      return parseFloat(n) == n;
    },

    function(n) {
      return (n - 0) == n && n.length > 0;
    },

    function(n) {
      return typeof n === 'number' && isFinite(n);
    },

    function(n) {
      return !Array.isArray(n) && !isNaN(parseFloat(n)) && isFinite(n.toString().replace(/^-/, ''));
    }

  ];


  // Examines the functionsToTest array, extracts the return statement of each function
  // and fills the toTest select element.
  var fillToTestSelect = function() {
    for (var i = 0; i < functionsToTest.length; i++) {
      var f = functionsToTest[i].toString();
      var option = /[\s\S]*return ([\s\S]*);/.exec(f)[1];
      $("#toTest").append('<option value="' + i + '">' + (i + 1) + '. ' + option + '</option>');
    }
  }

  var performTest = function(functionNumber) {
    reset(); // Reset previous test
    $("#tests").html(""); //Clean test results
    isNumber = functionsToTest[functionNumber]; // Override the isNumber global function with the one to test
    testSuite(); // Run the test

    // Get test results
    var totalFail = 0;
    var totalPass = 0;
    $("b.fail").each(function() {
      totalFail += Number($(this).html());
    });
    $("b.pass").each(function() {
      totalPass += Number($(this).html());
    });
    $("#testresult").html(totalFail + " of " + (totalFail + totalPass) + " test failed.");

    $("#banner").attr("class", "").addClass(totalFail > 0 ? "fail" : "pass");
  }

  return {
    performTest: performTest,
    fillToTestSelect: fillToTestSelect,
    testSuite: testSuite
  };
}();


$(document).ready(function() {
  testHelper.fillToTestSelect();
  testHelper.performTest(0);

  $("#toTest").change(function() {
    testHelper.performTest($(this).children(":selected").val());
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
<script src="https://rawgit.com/Xotic750/testrunner-old/master/testrunner.js" type="text/javascript"></script>
<link href="https://rawgit.com/Xotic750/testrunner-old/master/testrunner.css" rel="stylesheet" type="text/css">
<h1>isNumber Test Cases</h1>

<h2 id="banner" class="pass"></h2>

<h2 id="userAgent">Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11</h2>

<div id="currentFunction"></div>

<div id="selectFunction">
  <label for="toTest" style="font-weight:bold; font-size:Large;">Select function to test:</label>
  <select id="toTest" name="toTest">
  </select>
</div>

<div id="testCode"></div>

<ol id="tests">
  <li class="pass">
    <strong>Integer Literals <b style="color:black;">(0, 10, 10)</b></strong>

    <ol style="display: none;">
      <li class="pass">Negative integer string</li>

      <li class="pass">Zero string</li>

      <li class="pass">Positive integer string</li>

      <li class="pass">Negative integer number</li>

      <li class="pass">Zero integer number</li>

      <li class="pass">Positive integer number</li>

      <li class="pass">Octal integer literal string</li>

      <li class="pass">Octal integer literal</li>

      <li class="pass">Hexadecimal integer literal string</li>

      <li class="pass">Hexadecimal integer literal</li>
    </ol>
  </li>

  <li class="pass">
    <strong>Foating-Point Literals <b style="color:black;">(0, 6, 6)</b></strong>

    <ol style="display: none;">
      <li class="pass">Negative floating point string</li>

      <li class="pass">Positive floating point string</li>

      <li class="pass">Negative floating point number</li>

      <li class="pass">Positive floating point number</li>

      <li class="pass">Exponential notation</li>

      <li class="pass">Exponential notation string</li>
    </ol>
  </li>

  <li class="pass">
    <strong>Non-Numeric values <b style="color:black;">(0, 18, 18)</b></strong>

    <ol style="display: none;">
      <li class="pass">Empty string: false</li>

      <li class="pass">Whitespace characters string: false</li>

      <li class="pass">Tab characters string: false</li>

      <li class="pass">Alphanumeric character string: false</li>

      <li class="pass">Non-numeric character string: false</li>

      <li class="pass">Boolean true literal: false</li>

      <li class="pass">Boolean false literal: false</li>

      <li class="pass">Number with preceding non-numeric characters: false</li>

      <li class="pass">Number with trailling non-numeric characters: false</li>

      <li class="pass">Undefined value: false</li>

      <li class="pass">Null value: false</li>

      <li class="pass">NaN value: false</li>

      <li class="pass">Infinity primitive: false</li>

      <li class="pass">Positive Infinity: false</li>

      <li class="pass">Negative Infinity: false</li>

      <li class="pass">Date object: false</li>

      <li class="pass">Empty object: false</li>

      <li class="pass">Instance of a function: false</li>
    </ol>
  </li>
</ol>

<div id="main">
  This page contains tests for a set of isNumber functions. To see them, take a look at the source.
</div>

<div>
  <p class="result">Tests completed in 0 milliseconds.
    <br>0 tests of 0 failed.</p>
</div>


37



Oui, le built-in isNaN(object) sera beaucoup plus rapide que n'importe quelle analyse syntaxique, car elle est intégrée et compilée, au lieu d'être interprétée à la volée.

Bien que les résultats soient quelque peu différents de ce que vous cherchez (essayez-le):

                                              // IS NUMERIC
document.write(!isNaN('-1') + "<br />");      // true
document.write(!isNaN('-1.5') + "<br />");    // true
document.write(!isNaN('0') + "<br />");       // true
document.write(!isNaN('0.42') + "<br />");    // true
document.write(!isNaN('.42') + "<br />");     // true
document.write(!isNaN('99,999') + "<br />");  // false
document.write(!isNaN('0x89f') + "<br />");   // true
document.write(!isNaN('#abcdef') + "<br />"); // false
document.write(!isNaN('1.2.3') + "<br />");   // false
document.write(!isNaN('') + "<br />");        // true
document.write(!isNaN('blah') + "<br />");    // false

30



Depuis jQuery 1.7, vous pouvez utiliser jQuery.isNumeric():

$.isNumeric('-1');      // true
$.isNumeric('-1.5');    // true
$.isNumeric('0');       // true
$.isNumeric('0.42');    // true
$.isNumeric('.42');     // true
$.isNumeric('0x89f');   // true (valid hexa number)
$.isNumeric('99,999');  // false
$.isNumeric('#abcdef'); // false
$.isNumeric('1.2.3');   // false
$.isNumeric('');        // false
$.isNumeric('blah');    // false

Notez simplement que contrairement à ce que vous avez dit, 0x89f est un nombre valide (hexa)


14



Utilisez la fonction isNaN. Je crois que si vous testez pour !isNaN(yourstringhere) cela fonctionne bien pour l'une de ces situations.


14



Cela peut être fait sans RegExp

function IsNumeric(data){
    return parseFloat(data)==data;
}

10



Je réalise que la question originale ne mentionnait pas jQuery, mais si vous utilisez jQuery, vous pouvez faire:

$.isNumeric(val)

Simple.

https://api.jquery.com/jQuery.isNumeric/ (à partir de jQuery 1.7)


5