Frage Validieren von Dezimalzahlen in JavaScript - IsNumeric ()


Was ist der sauberste und 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

2148


Ursprung


Antworten:


@ Joels Antwort ist ziemlich nah, aber es wird in den folgenden Fällen fehlschlagen:

// 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 enthielt, unabhängig von seinem Typkönnte es ein sein String enthält einen numerischen Wert (ich musste auch exponentielle Notation berücksichtigen, usw.), a Number Englisch: www.db-artmag.de/2003/12/e/1/96.php Ich konnte praktisch alles an diese Funktion übergeben, ich konnte keine Typannahmen treffen, ich kümmerte mich nicht um Typzwang (z. B. +true == 1; aber true sollte nicht als betrachtet werden "numeric").

Ich denke, es lohnt sich, diesen Satz zu teilen +30 Komponententests gemacht zu zahlreichen Funktionsimplementierungen und teilen auch das, das alle meine Tests besteht:

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

P.S.  IsNaN & isFinite aufgrund der erzwungenen Umwandlung in eine Zahl ein verwirrendes Verhalten aufweisen. In ES6 Nummer.isNaN & Nummer.isFinite würde diese Probleme beheben. Berücksichtigen Sie dies bei der Verwendung.


Aktualisieren : Hier ist, wie jQuery es jetzt tut (2.2-stabil):

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

Aktualisieren : Eckig 4.3:

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

2756



Arrrgh! Höre nicht auf die regulären Antworten. RegEx ist eklig dafür, und ich spreche nicht nur Leistung. Es ist so einfach, subtile, unmögliche Fehler mit Ihrem regulären Ausdruck zu erkennen.

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

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

So funktioniert das:

Das (input - 0) Ausdruck erzwingt JavaScript, um die Eingabe für den Eingabewert einzugeben. es muss zuerst als eine Zahl für die Subtraktionsoperation interpretiert werden. Wenn diese Umwandlung in eine Zahl fehlschlägt, wird der Ausdruck zu NaN. Dies numerisch Das Ergebnis wird dann mit dem ursprünglichen Wert verglichen, den Sie übergeben haben. Da die linke Seite jetzt numerisch ist, wird erneut die Option "Zwang" verwendet. Jetzt, da die Eingabe von beiden Seiten vom selben ursprünglichen Wert auf den gleichen Typ gezwungen wurde, würden Sie denken, dass sie immer gleich sein sollten (immer wahr). Es gibt jedoch eine spezielle Regel, die besagt NaN ist nie gleich NaNDaher kann ein Wert, der nicht in eine Zahl konvertiert werden kann (und nur Werte, die nicht in Zahlen konvertiert werden können), zu false führen.

Die Überprüfung der Länge ist für einen Sonderfall mit leeren Strings. Beachten Sie auch, dass es auf Ihren 0x89f-Test fällt, aber das liegt daran, dass dies in vielen Umgebungen eine gute Möglichkeit ist, ein Zahlenliteral zu definieren. Wenn Sie dieses spezielle Szenario erfassen möchten, können Sie eine zusätzliche Überprüfung hinzufügen. Noch besser, wenn das der Grund ist, nicht zu benutzen isNaN() dann wickle einfach deine eigene Funktion herum isNaN() das kann auch die zusätzliche Überprüfung tun.

Zusammenfassend, Wenn Sie wissen möchten, ob ein Wert in eine Zahl konvertiert werden kann, versuchen Sie tatsächlich, ihn in eine Zahl umzuwandeln.


Ich ging zurück und recherchierte für Warum Eine Whitespace-Zeichenfolge hatte nicht die erwartete Ausgabe, und ich denke, ich bekomme es jetzt: eine leere Zeichenfolge wird erzwungen 0 eher, als NaN. Das Abschneiden der Zeichenfolge vor der Längenprüfung wird diesen Fall behandeln.

Das Ausführen der Unit-Tests gegen den neuen Code schlägt nur bei den Infinity- und booleschen Literalen fehl, und das einzige Problem, das auftritt, ist, wenn Sie Code generieren (wer würde wirklich ein Literal eingeben und prüfen, ob es numerisch ist? Du solltest kennt), und das wäre ein seltsamer Code zu generieren.

Aber wieder, Der einzige Grund, dies jemals zu verwenden, ist, wenn Sie aus irgendeinem Grund isNaN () vermeiden müssen.


314



Dieser Weg scheint gut zu funktionieren:

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

Und um es zu testen:

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

Ich habe diesen Regex ausgeliehen 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

58



Yahoo! Benutzeroberfläche benutzt das:

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

45



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

Dies funktioniert auch für 0x23 Typnummern.


44



Die angenommene Antwort scheiterte bei Test # 7 und ich schätze, das liegt daran, dass du deine Meinung geändert hast. Das ist also eine Antwort auf die angenommene Antwort, mit der ich Probleme hatte.

Während einiger Projekte musste ich einige Daten validieren und so sicher wie möglich sein, dass es ein numerischer Javascript-Wert ist, der in mathematischen Operationen verwendet werden kann.

jQuery und einige andere JavaScript-Bibliotheken enthalten bereits eine solche Funktion, die normalerweise aufgerufen wird isNumeric. Da ist auch ein Post auf Stackoverflow das wurde allgemein als die Antwort akzeptiert, die gleiche allgemeine Routine, die die oben erwähnten 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 dieses 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 ist es nicht numerisch.

Um dieses Problem zu beheben, habe ich eine Überprüfung hinzugefügt, um Arrays von der Logik zu diskontieren

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

Natürlich könntest du auch verwenden Array.isArray, Jquery $.isArray oder Prototyp Object.isArray Anstatt von Object.prototype.toString.call(n) !== '[object Array]'

Mein zweites Problem war, dass negative Hexadezimal-Ganzzahl-Literal-Strings ("-0xA" -> -10) nicht als numerisch gezählt wurden. Positive Hexadezimal-Ganzzahl-Literalzeichenfolgen ("0xA" -> 10) wurden jedoch als numerisch behandelt. Ich brauchte beides, um numerisch gültig zu sein.

Ich habe dann die Logik modifiziert, 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 bei jedem Aufruf der Funktion Sorgen um die Erstellung der Regex machen, können Sie sie innerhalb eines Closures 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 CMS genommen +30 Testfälle und klonte die Testen auf jsfiddle fügte meine zusätzlichen Testfälle und meine oben beschriebene Lösung hinzu.

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

BEARBEITEN: Wie von BergiEs gibt andere mögliche Objekte, die als numerisch betrachtet werden könnten, und es wäre besser, eine Whitelist als eine Blacklist zu verwenden. In diesem Sinne würde ich die Kriterien ergänzen.

Ich möchte, dass meine isNumeric-Funktion nur Zahlen oder Zeichenfolgen berücksichtigt

In diesem Sinne wäre es besser zu verwenden

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>


37



Ja, das eingebaute isNaN(object) wird viel schneller sein als jedes Regex-Parsing, weil es eingebaut und kompiliert ist, anstatt es im laufenden Betrieb zu interpretieren.

Obwohl die Ergebnisse etwas anders sind als das, wonach Sie suchen (Versuch es):

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



Seit jQuery 1.7 können Sie verwenden 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

Beachten Sie, dass, anders als was Sie gesagt haben, 0x89f ist eine gültige Nummer (Hexa)


14