Pertanyaan Validasi angka desimal dalam JavaScript - IsNumeric ()


Apa cara paling bersih dan paling efektif untuk memvalidasi angka desimal dalam JavaScript?

Poin bonus untuk:

  1. Kejelasan. Larutan harus bersih dan sederhana.
  2. Lintas-platform.

Uji kasus:

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


asal


Jawaban:


Jawaban @ Joel cukup dekat, tetapi akan gagal dalam kasus-kasus berikut:

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

Beberapa waktu lalu saya harus menerapkan IsNumeric berfungsi, untuk mengetahui apakah suatu variabel mengandung nilai numerik, terlepas dari tipenya, bisa jadi a String mengandung nilai numerik (saya harus mempertimbangkan juga notasi eksponensial, dll.), a Number objek, hampir semua hal dapat diteruskan ke fungsi itu, saya tidak bisa membuat asumsi jenis apa pun, mengurus pemaksaan jenis (mis. +true == 1; tapi true tidak boleh dianggap sebagai "numeric").

Saya pikir layak berbagi set ini +30 tes unit dibuat untuk berbagai fungsi implementasi, dan juga berbagi yang lulus semua tes saya:

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

P.S.  tidak ada & isFinite memiliki perilaku yang membingungkan karena konversi paksa ke nomor. Di ES6, Number.isNaN & Number.isFinite akan memperbaiki masalah ini. Ingatlah hal itu saat menggunakannya.


Memperbarui : Begini cara jQuery melakukannya sekarang (2,2-stabil):

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

Memperbarui : Angular 4.3:

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

2756



Arrrgh! Jangan dengarkan jawaban ekspresi reguler. RegEx adalah idola untuk ini, dan saya tidak berbicara hanya kinerja. Sangat mudah untuk membuat halus, tidak mungkin untuk melihat kesalahan dengan ekspresi reguler Anda.

Jika Anda tidak bisa menggunakannya isNaN(), ini seharusnya bekerja lebih baik:

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

Begini cara kerjanya:

Itu (input - 0) gaya ekspresi JavaScript untuk melakukan pemaksaan jenis pada nilai input Anda; pertama harus ditafsirkan sebagai angka untuk operasi pengurangan. Jika konversi ke nomor gagal, ekspresi akan menghasilkan NaN. Ini numerik Hasilnya kemudian dibandingkan dengan nilai asli yang Anda lewati. Karena sisi kiri sekarang numerik, pemaksaan jenis lagi digunakan. Sekarang bahwa masukan dari kedua sisi dipaksa untuk jenis yang sama dari nilai asli yang sama, Anda akan berpikir mereka harus selalu sama (selalu benar). Namun, ada aturan khusus yang mengatakan NaN tidak pernah sama dengan NaN, sehingga nilai yang tidak dapat dikonversi menjadi angka (dan hanya nilai yang tidak dapat dikonversi ke angka) akan menghasilkan false.

Pemeriksaan panjangnya untuk kasus khusus yang melibatkan string kosong. Juga perhatikan bahwa itu jatuh pada tes 0x89f Anda, tetapi itu karena di banyak lingkungan itu cara yang baik untuk mendefinisikan angka secara harfiah. Jika Anda ingin menangkap skenario spesifik tersebut, Anda dapat menambahkan pemeriksaan tambahan. Bahkan lebih baik, kalau itu alasan Anda untuk tidak menggunakan isNaN() lalu lilitkan saja fungsi Anda sendiri isNaN() yang juga bisa melakukan pemeriksaan tambahan.

Kesimpulan, jika Anda ingin tahu apakah suatu nilai dapat dikonversi menjadi angka, coba untuk mengubahnya menjadi angka.


Saya kembali dan melakukan penelitian untuk Mengapa string whitespace tidak memiliki output yang diharapkan, dan saya rasa saya mendapatkannya sekarang: string kosong dipaksa 0 daripada NaN. Cukup memangkas string sebelum pemeriksaan panjang akan menangani kasus ini.

Menjalankan tes unit terhadap kode baru dan hanya gagal pada infinity dan boolean literals, dan satu-satunya waktu yang seharusnya menjadi masalah adalah jika Anda membuat kode (benar-benar, siapa yang akan mengetikkan secara literal dan memeriksa apakah itu numerik? Kamu harus tahu), dan itu akan menjadi beberapa kode aneh untuk dihasilkan.

Tapi lagi, satu-satunya alasan untuk menggunakan ini adalah jika karena alasan tertentu Anda harus menghindari isNaN ().


314



Cara ini tampaknya berjalan dengan baik:

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

Dan untuk mengujinya:

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

Saya meminjam regex itu dari http://www.codetoad.com/javascript/isnumeric.asp. Penjelasan:

/^ 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 menggunakan ini:

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

45



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

Ini berfungsi untuk nomor tipe 0x23 juga.


44



Jawaban diterima gagal tes Anda # 7 dan saya kira itu karena Anda berubah pikiran. Jadi ini adalah jawaban atas jawaban yang diterima, yang mana saya punya masalah.

Selama beberapa proyek, saya perlu memvalidasi beberapa data dan memastikan bahwa itu adalah nilai numerik javascript yang dapat digunakan dalam operasi matematika.

jQuery, dan beberapa pustaka javascript lainnya sudah menyertakan fungsi seperti itu, biasanya disebut isNumeric. Ada juga a posting di stackoverflow yang telah diterima secara luas sebagai jawabannya, rutinitas umum yang sama yang digunakan oleh perpustakaan yang disebutkan sebelumnya.

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

Pertama, kode di atas akan menghasilkan true jika argumennya adalah array dengan panjang 1, dan bahwa elemen tunggal adalah tipe yang dianggap sebagai numerik oleh logika di atas. Menurut pendapat saya, jika itu array maka tidak numerik.

Untuk mengatasi masalah ini, saya menambahkan cek ke array diskon dari logika

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

Tentu saja, Anda juga bisa menggunakannya Array.isArray, jquery $.isArray atau prototipe Object.isArray dari pada Object.prototype.toString.call(n) !== '[object Array]'

Masalah kedua saya adalah bahwa string literal Heksadesimal Negatif negatif ("-0xA" -> -10) tidak dihitung sebagai numerik. Namun, string literal integer Heksadesimal positif ("0xA" -> 10) diperlakukan sebagai numerik. Saya perlu keduanya menjadi numerik yang valid.

Saya kemudian memodifikasi logika untuk mempertimbangkan hal ini.

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

Jika Anda khawatir tentang pembuatan regex setiap kali fungsi dipanggil maka Anda dapat menulis ulang dalam suatu penutupan, sesuatu seperti ini

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

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

Saya kemudian mengambil CMS +30 uji coba dan mengkloning pengujian pada jsfiddle menambahkan kasus uji tambahan saya dan solusi yang diuraikan di atas saya.

Ini mungkin tidak menggantikan jawaban yang diterima / digunakan secara luas tetapi jika ini lebih dari apa yang Anda harapkan sebagai hasil dari fungsi isnumerik Anda maka semoga ini akan membantu.

EDIT: Seperti yang ditunjukkan oleh Bergi, ada kemungkinan objek lain yang dapat dianggap numerik dan akan lebih baik untuk daftar putih daripada daftar hitam. Dengan pemikiran ini saya akan menambahkan kriteria.

Saya ingin fungsi isNumeric saya hanya mempertimbangkan Bilangan atau String

Dengan pemikiran ini, akan lebih baik untuk digunakan

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

Uji solusinya

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



Ya, built-in isNaN(object) akan jauh lebih cepat daripada parsing regex, karena itu built-in dan dikompilasi, bukan ditafsirkan dengan cepat.

Meskipun hasilnya agak berbeda dengan apa yang Anda cari (Cobalah):

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



Sejak jQuery 1.7, Anda bisa menggunakannya 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

Catat itu tidak seperti yang Anda katakan, 0x89f adalah angka yang valid (hexa)


14