2564 Stimmen

Dezimalzahlen in JavaScript validieren - IsNumeric()

Was ist der sauberste, effektivste Weg, um Dezimalzahlen in JavaScript zu validieren?

Bonuspunkte für:

  1. Klarheit. Die Lösung sollte sauber und einfach sein.
  2. Plattformübergreifend.

Testfälle:

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

273 Stimmen

Nur ein Hinweis 99,999 ist eine gültige Zahl in Frankreich, es ist das gleiche wie 99,999 in Großbritannien / USA-Format, so dass, wenn Sie in einer Zeichenfolge von sagen wir ein Eingabeformular lesen dann 99,999 kann wahr sein.

5 Stimmen

82 Stimmen

Dezimalkomma ist der Standard in ganz Europa und Russland (außer im Vereinigten Königreich)

2996voto

Christian C. Salvadó Punkte 763569

@Joel's Antwort ist ziemlich nahe dran, aber in den folgenden Fällen schlägt es fehl:

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

Vor einiger Zeit musste ich ein IsNumeric Funktion, um herauszufinden, ob eine Variable einen numerischen Wert enthält, unabhängig von ihrer Art könnte es sich um eine String die einen numerischen Wert enthält (ich musste auch die Exponentialschreibweise usw. berücksichtigen), eine Number Objekt kann praktisch alles an diese Funktion übergeben werden, ich kann keine Typannahmen treffen und muss mich um die Typenzwangskontrolle kümmern (z. B. +true == 1; sondern true sollte nicht als "numeric" ).

Ich denke, es lohnt sich, diese Reihe von +30 Einheitstests zu zahlreichen Funktionsimplementierungen gemacht, und auch diejenige, die alle meine Tests besteht, teilen:

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

P.D. isNaN & isFinite haben ein verwirrendes Verhalten aufgrund der erzwungenen Umwandlung in Zahlen. In ES6, Zahl.isNaN & Number.isFinite würde diese Probleme beheben. Behalten Sie das im Hinterkopf, wenn Sie sie verwenden.


Update : So macht es jQuery jetzt (2.2-stable) :

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

Update : Angular 4.3 :

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

0 Stimmen

jsBench sagt, dass angulars isNumeric 0,51% schneller ist als die Funktion isNaN && isFinite. Kann das jemand bestätigen? Kann meinen Test ohne Registrierung nicht teilen...

356voto

Joel Coehoorn Punkte 377088

Arrrgh! Hören Sie nicht auf die Antworten der regulären Ausdrücke. RegEx ist ekelhaft, und damit meine ich nicht nur die Leistung. Es ist so einfach, subtile Fehler mit regulären Ausdrücken zu machen, die unmöglich zu erkennen sind.

Wenn Sie nicht verwenden können isNaN() sollte dies viel besser funktionieren:

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

Und so funktioniert es:

Le site (input - 0) Ausdruck zwingt JavaScript dazu, den Eingabewert in einen anderen Typ umzuwandeln; er muss zunächst als Zahl für die Subtraktionsoperation interpretiert werden. Wenn die Umwandlung in eine Zahl fehlschlägt, führt der Ausdruck zu NaN . Diese numerisch Das Ergebnis wird dann mit dem ursprünglichen Wert verglichen, den Sie eingegeben haben. Da die linke Seite nun numerisch ist, wird wieder die Typenzwangssteuerung verwendet. Da die Eingaben auf beiden Seiten vom gleichen Typ sind und vom gleichen Ausgangswert stammen, sollte man meinen, dass sie immer gleich sind (immer wahr). Es gibt jedoch eine spezielle Regel, die besagt NaN ist niemals gleich NaN Ein Wert, der nicht in eine Zahl umgewandelt werden kann (und nur Werte, die nicht in Zahlen umgewandelt werden können), führt also zu false.

Die Überprüfung der Länge gilt für den Sonderfall der leeren Zeichenketten. Beachten Sie auch, dass es fällt auf Ihre 0x89f Test, aber das ist, weil in vielen Umgebungen, die eine okaye Art und Weise zu definieren, ein Zahlenliteral ist. Wenn Sie dieses spezielle Szenario abfangen wollen, können Sie eine zusätzliche Prüfung hinzufügen. Noch besser wäre es, wenn das der Grund für die Nichtverwendung von isNaN() dann wickeln Sie einfach Ihre eigene Funktion um isNaN() die auch die zusätzliche Prüfung durchführen können.

Zusammengefasst, Wenn Sie wissen wollen, ob ein Wert in eine Zahl umgewandelt werden kann, versuchen Sie, ihn tatsächlich in eine Zahl umzuwandeln.


Ich habe noch einmal nachgeforscht für pourquoi eine Zeichenkette mit Leerzeichen hatte nicht die erwartete Ausgabe, und ich glaube, ich habe es jetzt verstanden: eine leere Zeichenkette wird gezwungenermaßen zu 0 statt NaN . In diesem Fall genügt es, die Zeichenfolge vor der Längenprüfung zu kürzen.

Wenn man die Unit-Tests gegen den neuen Code laufen lässt, scheitert es nur an den Unendlichkeits- und booleschen Literalen, und das einzige Mal, dass das ein Problem sein sollte, ist, wenn man Code generiert (wirklich, wer würde ein Literal eingeben und prüfen, ob es numerisch ist? Sie sollten wissen ), und das wäre ein seltsamer Code, den man erzeugen müsste.

Aber noch einmal, der einzige Grund, dies zu verwenden, ist, wenn Sie aus irgendeinem Grund isNaN() vermeiden müssen.

85voto

Michael Haren Punkte 101002

Dieser Weg scheint gut zu funktionieren:

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

In einer Zeile:

const IsNumeric = (num) => /^-{0,1}\d*\.{0,1}\d+$/.test(num);

Und um es zu testen:

const IsNumeric = (num) => /^-{0,1}\d*\.{0,1}\d+$/.test(num);

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

console.log(TestIsNumeric());

.as-console-wrapper { max-height: 100% !important; top: 0; }

Diese Regex habe ich mir von http://www.codetoad.com/javascript/isnumeric.asp . Erläuterung:

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

58voto

camomileCase Punkte 1665

Yahoo! UI verwendet dies:

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

56voto

Xotic750 Punkte 21758

Die akzeptierte Antwort hat Ihren Test #7 nicht bestanden, und ich vermute, das liegt daran, dass Sie Ihre Meinung geändert haben. Dies ist also eine Antwort auf die akzeptierte Antwort, mit der ich Probleme hatte.

Während einiger Projekte musste ich einige Daten validieren und so sicher wie möglich sein, dass es sich um einen numerischen Wert in Javascript handelt, der für mathematische Operationen verwendet werden kann.

jQuery und einige andere Javascript-Bibliotheken enthalten bereits eine solche Funktion, die normalerweise isNumeric . Es gibt auch eine Beitrag auf Stackoverflow die weithin als die Antwort akzeptiert wurde, die gleiche allgemeine Routine, die die oben genannten Bibliotheken verwenden.

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

Erstens würde der obige Code true zurückgeben, wenn das Argument ein Array der Länge 1 wäre und das einzelne Element von einem Typ wäre, der von der obigen Logik als numerisch angesehen wird. Meiner Meinung nach, wenn es ein Array ist dann seine nicht numerisch.

Um dieses Problem zu lindern, habe ich eine Prüfung hinzugefügt, um Arrays von der Logik auszuschließen

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

Natürlich können Sie auch Array.isArray , jquery $.isArray oder Prototyp Object.isArray anstelle von Object.prototype.toString.call(n) !== '[object Array]'

Das zweite Problem war, dass negative hexadezimale Ganzzahl-Zeichenfolgen ("-0xA" -> -10) nicht als numerisch gezählt wurden. Positive hexadezimale Ganzzahl-Literalzeichenketten ("0xA" -> 10) wurden jedoch als numerisch behandelt. Ich brauchte beides, um als numerisch zu gelten.

Ich habe dann die Logik geändert, um dies zu berücksichtigen.

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

Wenn Sie sich Sorgen über die Erstellung der Regex bei jedem Funktionsaufruf machen, könnten Sie sie innerhalb einer Schließung umschreiben, etwa so

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

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

Ich habe dann CMSs genommen +30 Testfälle und klonte die Testen auf jsfiddle meine zusätzlichen Testfälle und meine oben beschriebene Lösung hinzugefügt.

Es kann nicht die weithin akzeptierte/verwendete Antwort ersetzen, aber wenn dies mehr von dem ist, was Sie als Ergebnisse von Ihrer isNumeric-Funktion erwarten, dann wird dies hoffentlich von einiger Hilfe sein.

EDIT: Wie von Bergi Es gibt noch andere mögliche Objekte, die als numerisch angesehen werden könnten, und es wäre besser, sie auf die Whitelist zu setzen als auf die Blacklist. In diesem Sinne würde ich die Kriterien ergänzen.

Ich möchte meine isNumeric-Funktion nur Zahlen oder Zeichenfolgen berücksichtigen

In diesem Sinne wäre es besser, wenn Sie

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

Testen Sie die Lösungen

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>

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X