Vraag Valideer decimale getallen in JavaScript - IsNumeric ()


Wat is de schoonste, meest effectieve manier om decimale getallen in JavaScript te valideren?

Bonuspunten voor:

  1. Duidelijkheid. Oplossing moet schoon en eenvoudig zijn.
  2. Cross-platform.

Testgevallen:

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


oorsprong


antwoorden:


@ Joel's antwoord is redelijk dichtbij, maar het zal mislukken in de volgende gevallen:

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

Enige tijd geleden moest ik een IsNumeric functie, om te achterhalen of een variabele een numerieke waarde bevatte, ongeacht het type, het kan een zijn String met een numerieke waarde (ik moest ook een exponentiële notatie overwegen, enz.), a Number object, vrijwel alles kon aan die functie worden doorgegeven, ik kon geen enkele type aanname doen, zorgde voor type dwang (bijv. +true == 1; maar true moet niet worden beschouwd als "numeric").

Ik denk dat het de moeite waard is om deze set te delen +30 eenheidstests gemaakt om verschillende functie-implementaties, en ook degene die al mijn tests doorgeeft delen:

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

Postscriptum  isNaN & isFinite hebben een verwarrend gedrag als gevolg van gedwongen conversie naar nummer. In ES6, Number.isNaN & Number.isFinite zou deze problemen oplossen. Houd dat in gedachten wanneer u ze gebruikt.


Bijwerken : Hier is hoe jQuery het nu doet (2.2-stabiel):

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

Bijwerken : Hoekig 4.3:

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

2756



Arrrgh! Luister niet naar de antwoorden van de reguliere expressie. RegEx is hiervoor icky, en ik heb het niet alleen over prestaties. Het is zo eenvoudig om subtiel, onmogelijk om fouten te maken met je reguliere expressie te maken.

Als je niet kunt gebruiken isNaN(), dit zou veel beter moeten werken:

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

Dit is hoe het werkt:

De (input - 0) expressie dwingt JavaScript om te typen dwang op uw invoerwaarde; het moet eerst worden geïnterpreteerd als een getal voor de aftrekbewerking. Als die conversie naar een nummer mislukt, zal de uitdrukking resulteren in NaN. Deze numerieke Het resultaat wordt vervolgens vergeleken met de oorspronkelijke waarde die u hebt ingevoerd. Omdat de linkerkant nu numeriek is, wordt het type dwang opnieuw gebruikt. Nu dat de invoer van beide kanten was afgedwongen naar hetzelfde type van dezelfde oorspronkelijke waarde, zou je denken dat ze altijd hetzelfde zouden moeten zijn (altijd waar). Er is echter een speciale regel die zegt NaN is nooit gelijk aan NaNen dus een waarde die niet naar een getal kan worden geconverteerd (en alleen waarden die niet naar getallen kunnen worden geconverteerd) resulteert in false.

De controle van de lengte is voor een speciaal geval met lege reeksen. Merk ook op dat het neerkomt op je 0x89f-test, maar dat komt omdat in veel omgevingen een goede manier is om een ​​letterlijke getal te definiëren. Als u dat specifieke scenario wilt vangen, kunt u een extra controle toevoegen. Sterker nog, als dat je reden is om het niet te gebruiken isNaN() verpak dan gewoon je eigen functie isNaN() dat kan ook de extra controle uitvoeren.

Samengevat, als je wilt weten of een waarde naar een getal kan worden geconverteerd, probeer het dan om te zetten in een getal.


Ik ging terug en deed wat onderzoek voor waarom een whitespace string had niet de verwachte output, en ik denk dat ik het nu krijg: een lege string is gedwongen 0 liever dan NaN. Gewoon het touw snoeien voordat de lengtest deze zaak behandelt.

Het uitvoeren van de unit test tegen de nieuwe code en het faalt alleen op de oneindige en booleaanse letterlijke waarden, en de enige tijd die een probleem zou moeten zijn, is als je code genereert (eigenlijk, wie zou een letterlijke intypen en controleren of het numeriek is? Je zou moeten weten), en dat zou een vreemde code zijn om te genereren.

Maar nogmaals, de enige reden om dit te gebruiken is om om een ​​of andere reden isNaN () te vermijden.


314



Op deze manier lijkt het goed te werken:

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

En om het te 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;
}

Ik heb die regex geleend van http://www.codetoad.com/javascript/isnumeric.asp. Uitleg:

/^ 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 gebruikt dit:

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

45



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

Dit werkt ook voor 0x23 typenummers.


44



Het geaccepteerde antwoord is mislukt voor test # 7 en ik denk dat dit komt omdat je van gedachten bent veranderd. Dus dit is een antwoord op het geaccepteerde antwoord, waarmee ik problemen had.

Tijdens sommige projecten moest ik sommige gegevens valideren en zo zeker mogelijk zijn dat het een javascript numerieke waarde is die kan worden gebruikt in wiskundige bewerkingen.

jQuery en een aantal andere javascript-bibliotheken hebben al zo'n functie, meestal genoemd isNumeric. Er is ook een post op stackoverflow dat is algemeen aanvaard als het antwoord, dezelfde algemene routine die de eerder genoemde bibliotheken gebruiken.

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

Ten eerste zou de bovenstaande code true opleveren als het argument een array van lengte 1 was, en dat enkele element van een type was dat door bovenstaande logica als numeriek werd beschouwd. Naar mijn mening, als het een array is, dan is het niet numeriek.

Om dit probleem te verlichten, voegde ik een cheque toe aan kortingsmatrices uit de logica

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

Natuurlijk kunt u ook gebruiken Array.isArray, jQuery $.isArray of prototype Object.isArray in plaats van Object.prototype.toString.call(n) !== '[object Array]'

Mijn tweede probleem was dat negatieve hexadecimale letterlijke tekenreeksen ("-0xA" -> -10) niet als numeriek werden geteld. Positieve hexadecimale letterlijke tekenreeksen ("0xA" -> 10) werden echter als numeriek behandeld. Ik moest beide geldig numeriek zijn.

Vervolgens heb ik de logica aangepast om hier rekening mee te houden.

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

Als je je zorgen maakt over het maken van de regex elke keer dat de functie wordt aangeroepen, kun je deze binnen een afsluiting herschrijven, zoiets als dit

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

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

Ik nam toen CMSs +30 testgevallen en de klonen testen op jsfiddle mijn extra testgevallen en mijn hierboven beschreven oplossing toegevoegd.

Het kan het algemeen aanvaarde / gebruikte antwoord niet vervangen, maar als dit meer is wat u verwacht als resultaten van uw isNumerieke functie, dan zal dit hopelijk enige hulp bieden.

BEWERK: Zoals aangegeven door Bergi, er zijn andere mogelijke objecten die als numeriek kunnen worden beschouwd en het zou beter zijn om op de witte lijst te staan ​​dan op de zwarte lijst. Met dit in gedachten zou ik aan de criteria toevoegen.

Ik wil dat mijn isNumerieke functie alleen getallen of strings overweegt

Met dit in gedachten zou het beter zijn om te gebruiken

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

Test de oplossingen

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, de ingebouwde isNaN(object) zal veel sneller zijn dan elke regex-parsing, omdat het is ingebouwd en gecompileerd, in plaats van on-the-fly te worden geïnterpreteerd.

Hoewel de resultaten enigszins verschillen van wat u zoekt (probeer het):

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



Sinds jQuery 1.7 kunt u gebruiken 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

Houd er rekening mee dat in tegenstelling tot wat je zei, 0x89f is een geldig nummer (hexa)


14



Gebruik de functie isNaN. Ik geloof dat als je test voor !isNaN(yourstringhere) het werkt prima voor elk van deze situaties.


14



Het kan zonder RegExp worden gedaan als

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

10



Ik realiseer me dat de oorspronkelijke vraag jQuery niet vermeldde, maar als je jQuery gebruikt, kun je het volgende doen:

$.isNumeric(val)

Eenvoudig.

https://api.jquery.com/jQuery.isNumeric/ (vanaf jQuery 1.7)


5