Domanda Convalidare i numeri decimali in JavaScript - IsNumeric ()


Qual è il modo più pulito ed efficace per convalidare i numeri decimali in JavaScript?

Punti bonus per:

  1. Chiarezza. La soluzione dovrebbe essere pulita e semplice.
  2. Cross-platform.

Casi 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


risposte:


@ La risposta di Joel è abbastanza vicino, ma fallirà nei seguenti casi:

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

Qualche tempo fa ho dovuto implementare un IsNumeric funzione, per scoprire se una variabile conteneva un valore numerico, indipendentemente dal suo tipo, potrebbe essere un String contenente un valore numerico (dovevo considerare anche la notazione esponenziale, ecc.), a Number oggetto, praticamente qualsiasi cosa potrebbe essere passata a quella funzione, non potevo fare alcun tipo di ipotesi, avendo cura della coercizione di tipo (es. +true == 1; ma true non dovrebbe essere considerato come "numeric").

Penso valga la pena condividere questo insieme di +30 test unitari fatto per numerose implementazioni di funzioni, e anche condividere quello che supera tutti i miei test:

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

Post scriptum  isNaN & isFinite avere un comportamento confuso dovuto alla conversione forzata in numero. In ES6, Number.isNaN & Number.isFinite risolverebbe questi problemi. Tienilo a mente quando li usi.


Aggiornare : Ecco come jQuery lo fa ora (2.2-stabile):

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

Aggiornare : Angolare 4.3:

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

2756



Arrrgh! Non ascoltare le risposte alle espressioni regolari. RegEx è icky per questo, e non sto parlando solo delle prestazioni. È così facile rendere sottile, impossibile individuare gli errori con la tua espressione regolare.

Se non puoi usare isNaN(), questo dovrebbe funzionare molto meglio:

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

Ecco come funziona:

Il (input - 0) expression forza JavaScript a fare tipo di coercizione sul valore di input; deve prima essere interpretato come un numero per l'operazione di sottrazione. Se la conversione in un numero fallisce, l'espressione risulterà in NaN. Questo numerico il risultato viene quindi confrontato con il valore originale passato. Poiché il lato sinistro è ora numerico, viene nuovamente utilizzata la coercizione di tipo. Ora che l'input da entrambi i lati è stato forzato allo stesso tipo dello stesso valore originale, si potrebbe pensare che dovrebbero essere sempre gli stessi (sempre vero). Tuttavia, c'è una regola speciale che dice NaN non è mai uguale a NaNe quindi un valore che non può essere convertito in un numero (e solo i valori che non possono essere convertiti in numeri) risulterà falso.

Il controllo sulla lunghezza è per un caso speciale che coinvolge stringhe vuote. Si noti inoltre che cade giù sul test 0x89f, ma questo perché in molti ambienti è un modo corretto per definire un numero letterale. Se vuoi cogliere uno scenario specifico, puoi aggiungere un ulteriore controllo. Ancora meglio, se questa è la tua ragione per non usarla isNaN() quindi avvolgi semplicemente la tua funzione isNaN() che può anche fare il controllo aggiuntivo.

In sintesi, se vuoi sapere se un valore può essere convertito in un numero, in realtà prova a convertirlo in un numero.


Sono tornato e ho fatto qualche ricerca per perché una stringa di spaziatura non ha avuto l'output atteso e penso di ottenerla ora: una stringa vuota è forzata a 0 piuttosto che NaN. Basta tagliare la stringa prima che il controllo della lunghezza gestisca questo caso.

Eseguendo l'unit test contro il nuovo codice, fallisce solo sui letterali infinito e booleano e l'unica volta che dovrebbe essere un problema è se stai generando codice (in realtà, chi digita un letterale e controlla se è numerico? Dovresti conoscere), e quello sarebbe un codice strano da generare.

Ma, ancora, l'unica ragione per cui è possibile utilizzare questo è se per qualche motivo devi evitare isNaN ().


314



In questo modo sembra funzionare bene:

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

E per testarlo:

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

Ho preso in prestito quella regex da http://www.codetoad.com/javascript/isnumeric.asp. Spiegazione:

/^ 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 usa questo:

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

45



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

Funziona anche per i numeri di tipo 0x23.


44



La risposta accettata ha fallito il test n. 7 e immagino sia perché hai cambiato idea. Quindi questa è una risposta alla risposta accettata, con la quale ho avuto problemi.

Durante alcuni progetti ho avuto bisogno di convalidare alcuni dati e di essere il più certo possibile che si tratti di un valore numerico javascript che può essere utilizzato nelle operazioni matematiche.

jQuery e alcune altre librerie javascript includono già tale funzione, solitamente chiamata isNumeric. C'è anche un pubblicare su stackoverflow che è stata ampiamente accettata come risposta, la stessa routine generale che le librerie citate stanno usando.

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

Innanzitutto, il codice precedente restituirebbe true se l'argomento era una matrice di lunghezza 1 e quell'elemento singolo era di un tipo ritenuto numerico dalla logica precedente. A mio parere, se si tratta di un array, non è numerico.

Per alleviare questo problema, ho aggiunto un controllo per scartare gli array dalla logica

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

Certo, potresti anche usarlo Array.isArray, jquery $.isArray o prototipo Object.isArray invece di Object.prototype.toString.call(n) !== '[object Array]'

Il mio secondo problema era che le stringhe letterali esadecimali negative ("-0xA" -> -10) non venivano contate come numeriche. Tuttavia, le stringhe letterali esadecimali positive ("0xA" -> 10) sono state trattate come numeriche. Avevo bisogno che entrambi fossero validi numerici.

Ho quindi modificato la logica per tenerne conto.

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

Se sei preoccupato della creazione della regex ogni volta che viene chiamata la funzione, puoi riscriverla all'interno di una chiusura, qualcosa come questa

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

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

Ho quindi preso CMS +30 casi di test e clonato il test su jsfiddle aggiunti i miei casi di test aggiuntivi e la mia soluzione sopra descritta.

Non può sostituire la risposta largamente accettata / usata, ma se questo è più di ciò che ti aspetti come risultato della tua funzione isNumeric, allora spero che questo sia di qualche aiuto.

MODIFICARE: Come sottolineato da Bergi, ci sono altri possibili oggetti che potrebbero essere considerati numerici e sarebbe meglio fare una lista bianca della lista nera. Con questo in mente vorrei aggiungere ai criteri.

Voglio che la mia funzione isNumeric consideri solo numeri o stringhe

Con questo in mente, sarebbe meglio usare

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(/^-/, ''));
}

Prova le soluzioni

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



Sì, il built-in isNaN(object) sarà molto più veloce di qualsiasi analisi regex, perché è integrata e compilata, invece di essere interpretata al volo.

Anche se i risultati sono leggermente diversi da quello che stai cercando (Provalo):

                                              // 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



Dal momento che jQuery 1.7, è possibile utilizzare 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

Nota che a differenza di quello che hai detto, 0x89f è un numero valido (hexa)


14



Usa la funzione isNaN. Credo che se si prova per !isNaN(yourstringhere) funziona bene per ognuna di queste situazioni.


14