Xác thực rằng một chuỗi là một số nguyên dương


200

Tôi muốn thử nghiệm không an toàn đơn giản nhất để kiểm tra xem một chuỗi trong JavaScript có phải là số nguyên dương hay không.

isNaN(str)trả về true cho tất cả các loại giá trị không nguyên và parseInt(str)đang trả về số nguyên cho chuỗi float, như "2.5". Và tôi cũng không muốn phải sử dụng một số plugin jQuery.


7
Bạn có cho phép "+2" không? Làm thế nào về "0,1e1"? Làm thế nào về "1.0000"?
Phrogz

Các isNaN()chức năng xử theo cách nó vì quan niệm Not a Numbercó một ý nghĩa rất cụ thể trong IEEE 794 đặc điểm kỹ thuật dấu chấm động. Nó không có ý định cung cấp một câu trả lời cho câu hỏi thông tục đơn giản, "giá trị này không phải là một con số?"
Pointy

3
Câu hỏi mơ hồ đến cùng cực. Bạn không thể xác thực rằng "một chuỗi là một số nguyên", bởi vì không có điều đó - không có đối tượng nào có thể là một chuỗi và một số cùng một lúc. Bạn có thể có nghĩa là "làm thế nào để kiểm tra xem một chuỗi có phải là đại diện hợp lệ của một số nguyên không", nhưng để trả lời điều này, chúng ta cần biết ngôn ngữ hoặc "văn hóa" mà bạn đang nói đến. Ví dụ: ٢‎٣٤hoặc MCMXIXlà cả hai biểu diễn số nguyên hợp lệ, nhưng tôi không nghĩ rằng bạn đang tìm kiếm một mã có thể phân tích các mã này. Bạn có thể chỉ định định dạng số nào bạn sẽ hỗ trợ không, vì mọi người có vẻ bối rối về điều đó.
georg

5
Tôi nghĩ rằng 'mơ hồ đến cùng cực' là một chút cực đoan; nhưng dù sao đi nữa ... Bối cảnh là xác nhận trường nhập số lượng trên giỏ hàng được sử dụng ở một quốc gia nói tiếng Anh, vì vậy chỉ có các chữ số phương Tây. Bởi "tích cực" tôi có nghĩa là lớn hơn không. Tôi có chấp nhận như sau: "+2" có, "0.1e1" không, "1.000" tại sao không. Tuy nhiên, nếu bạn có thể cung cấp một câu trả lời có thể được điều chỉnh để bao gồm / loại trừ các tình huống chuyên biệt khác nhau này, tôi hứa sẽ cung cấp cho bạn thêm (và tôi chắc chắn những người khác cũng sẽ như vậy).
Mick Byrne

Câu trả lời:


296

Hai câu trả lời cho bạn:

  • Dựa trên phân tích cú pháp

  • Biểu hiện thông thường

Lưu ý rằng trong cả hai trường hợp, tôi đã giải thích "số nguyên dương" để bao gồm 0, mặc dù 0không tích cực. Tôi bao gồm ghi chú nếu bạn muốn không cho phép 0.

Dựa trên phân tích cú pháp

Nếu bạn muốn nó là một chuỗi số nguyên thập phân được chuẩn hóa trên một phạm vi giá trị hợp lý, bạn có thể làm điều này:

function isNormalInteger(str) {
    var n = Math.floor(Number(str));
    return n !== Infinity && String(n) === str && n >= 0;
}

hoặc nếu bạn muốn cho phép khoảng trắng và số 0 đứng đầu:

function isNormalInteger(str) {
    str = str.trim();
    if (!str) {
        return false;
    }
    str = str.replace(/^0+/, "") || "0";
    var n = Math.floor(Number(str));
    return n !== Infinity && String(n) === str && n >= 0;
}

Thử nghiệm trực tiếp (không xử lý các số 0 hoặc khoảng trắng hàng đầu):

Thử nghiệm trực tiếp ( với việc xử lý các số 0 và khoảng trắng hàng đầu):

Nếu bạn muốn không cho phép 0, chỉ cần thay đổi >= 0thành > 0. (Hoặc, trong phiên bản cho phép các số 0 đứng đầu, hãy xóa dòng || "0"trên replace.)

Cách thức hoạt động:

  1. Trong phiên bản cho phép khoảng trắng và số 0 đứng đầu:

    • str = str.trim(); loại bỏ bất kỳ whitepace hàng đầu và dấu.
    • if (!str) bắt một chuỗi trống và trả về, không có điểm nào trong phần còn lại của công việc.
    • str = str.replace(/^0+/, "") || "0"; xóa tất cả các số 0 đứng đầu khỏi chuỗi - nhưng nếu điều đó dẫn đến một chuỗi trống, hãy khôi phục một số 0 duy nhất.
  2. Number(str): Chuyển đổi strthành một số; con số có thể có một phần phân số, hoặc có thể NaN.

  3. Math.floor: Cắt bớt số (cắt bỏ bất kỳ phần phân số nào).

  4. String(...): Chuyển đổi kết quả trở lại thành một chuỗi thập phân bình thường. Đối với những con số thực sự lớn, điều này sẽ đi đến ký hiệu khoa học, có thể phá vỡ phương pháp này. (Tôi không biết chắc đâu là sự chia rẽ, các chi tiết trong spec , nhưng đối với toàn bộ số liệu mà tôi tin rằng đó là tại thời điểm bạn đã vượt quá 21 chữ số [bởi thời gian đó số đã trở nên rất không chính xác, như IEEE-754 các số chính xác kép chỉ có 15 chữ số chính xác ..)

  5. ... === str: So sánh với chuỗi ban đầu.

  6. n >= 0: Kiểm tra xem nó có tích cực không.

Lưu ý rằng điều này không thành công cho đầu vào "+1", bất kỳ đầu vào nào trong ký hiệu khoa học không quay lại cùng một ký hiệu khoa học ở String(...)giai đoạn và đối với bất kỳ giá trị nào mà loại số JavaScript sử dụng (điểm nổi nhị phân chính xác kép của IEEE-754) không thể biểu thị chính xác phân tích nào gần với một giá trị khác với giá trị đã cho (bao gồm nhiều số nguyên trên 9,007,199,254,740,992; chẳng hạn, 1234567890123456789sẽ thất bại). Cái trước là một sửa chữa dễ dàng, hai cái sau không quá nhiều.

Biểu hiện thông thường

Cách tiếp cận khác là kiểm tra các ký tự của chuỗi thông qua biểu thức chính quy, nếu mục tiêu của bạn là chỉ cho phép (nói) một tùy chọn +theo sau bởi một 0hoặc một chuỗi ở định dạng thập phân bình thường:

function isNormalInteger(str) {
    return /^\+?(0|[1-9]\d*)$/.test(str);
}

Thử nghiệm trực tiếp:

Cách thức hoạt động:

  1. ^: Khớp bắt đầu chuỗi

  2. \+?: Cho phép một, tùy chọn +(xóa cái này nếu bạn không muốn)

  3. (?:...|...): Cho phép một trong hai tùy chọn này (không tạo nhóm chụp):

    1. (0|...): Cho phép 0tự ...

    2. (...|[1-9]\d*): ... hoặc một số bắt đầu bằng một số khác 0và theo sau là bất kỳ số chữ số thập phân nào.

  4. $: Kết thúc chuỗi kết thúc.

Nếu bạn muốn không cho phép 0(vì nó không tích cực), biểu thức chính quy sẽ trở thành /^\+?[1-9]\d*$/(ví dụ: chúng ta có thể mất đi sự thay thế mà chúng ta cần cho phép 0).

Nếu bạn muốn cho phép các số 0 đứng đầu (0123, 00524), thì chỉ cần thay thế xen kẽ (?:0|[1-9]\d*)bằng\d+

function isNormalInteger(str) {
    return /^\+?\d+$/.test(str);
}

Nếu bạn muốn cho phép khoảng trắng, hãy thêm \s*ngay sau ^\s*ngay trước đó $.

Lưu ý khi bạn chuyển đổi số đó thành số: Trên các động cơ hiện đại, có thể sử dụng +strhoặc Number(str)thực hiện nó là tốt, nhưng những cái cũ hơn có thể mở rộng chúng theo cách không chuẩn (nhưng trước đây được cho phép) có nghĩa là số 0 đứng đầu có nghĩa là bát phân (cơ sở 8), ví dụ "010" => 8. Khi bạn đã xác thực số này, bạn có thể sử dụng một cách an toàn parseInt(str, 10)để đảm bảo rằng nó được phân tích cú pháp dưới dạng thập phân (cơ sở 10). parseIntsẽ bỏ qua rác ở cuối chuỗi, nhưng chúng tôi đảm bảo không có bất kỳ thứ gì với regex.


cả hai Number(' ')Number('')trở lại 0trong khi nên trở lại NaNthay thế.
neurino

@TJCrowder ok nhưng tôi cần các chuỗi như '' 2a '' để bị từ chối bằng cách nào đó, parseInttrả về 2.
neurino

@neurino: /\D/.test('2a')là đúng (vì không có chữ số). Vì vậy, có lẽ if (!/\D/.test(str) && !isNan((num = parseInt(str, 10))) { /* Valid number in num */}... Chỉ cần ra khỏi đỉnh đầu của tôi ...
TJ Crowder

Hiệu suất khôn ngoan, phương pháp nào sẽ nhanh hơn? Hoặc cái nào được khuyến khích?
phkavitha

@phkavitha: Câu trả lời cho các câu hỏi về hiệu suất JavaScript hầu như luôn luôn là "Nó phụ thuộc" bởi vì các công cụ khác nhau rất khác nhau. Bạn có thể kiểm tra các công cụ / trình duyệt mục tiêu của mình bằng jsperf.com . Tôi không nghĩ rằng hoạt động này có thể sẽ là nút cổ chai trong bất kỳ ứng dụng nào ... :-)
TJ Crowder

76

Giải pháp 1

Nếu chúng tôi coi số nguyên JavaScript là giá trị tối đa 4294967295(nghĩa là Math.pow(2,32)-1), thì giải pháp ngắn sau đây sẽ hoạt động hoàn hảo:

function isPositiveInteger(n) {
    return n >>> 0 === parseFloat(n);
}

SỰ MIÊU TẢ:

  1. Toán tử thay đổi quyền điền không thực hiện ba điều quan trọng:
    • cắt phần thập phân
      • 123.45 >>> 0 === 123
    • sự thay đổi cho số âm
      • -1 >>> 0 === 4294967295
    • "công trình" trong phạm vi MAX_INT
      • 1e10 >>> 0 === 1410065408
      • 1e7 >>> 0 === 10000000
  2. parseFloatkhông phân tích chính xác các số chuỗi (cài đặt NaNcho các chuỗi không số)

KIỂM TRA:

"0"                     : true
"23"                    : true
"-10"                   : false
"10.30"                 : false
"-40.1"                 : false
"string"                : false
"1234567890"            : true
"129000098131766699.1"  : false
"-1e7"                  : false
"1e7"                   : true
"1e10"                  : false
"1edf"                  : false
" "                     : false
""                      : false

DEMO: http://jsfiddle.net/5UCy4/37/


Giải pháp 2

Một cách khác là tốt cho tất cả các giá trị số có giá trị đến Number.MAX_VALUE, tức là về 1.7976931348623157e+308:

function isPositiveInteger(n) {
    return 0 === n % (!isNaN(parseFloat(n)) && 0 <= ~~n);
}

SỰ MIÊU TẢ:

  1. !isNaN(parseFloat(n))được sử dụng để lọc tinh khiết giá trị chuỗi, ví dụ như "", " ", "string";
  2. 0 <= ~~nlọc tiêu cực và giá trị phi số nguyên lớn, ví dụ như "-40.1", "129000098131766699";
  3. (!isNaN(parseFloat(n)) && 0 <= ~~n)trả về truenếu giá trị là cả sốdương ;
  4. 0 === n % (...)kiểm tra nếu giá trị không nổi - ở đây (...)(xem 3) được đánh giá như 0trong trường hợp falsevà như 1trong trường hợp true.

KIỂM TRA:

"0"                     : true
"23"                    : true
"-10"                   : false
"10.30"                 : false
"-40.1"                 : false
"string"                : false
"1234567890"            : true
"129000098131766699.1"  : false
"-1e10"                 : false
"1e10"                  : true
"1edf"                  : false
" "                     : false
""                      : false

DEMO: http://jsfiddle.net/5UCy4/14/


Phiên bản trước:

function isPositiveInteger(n) {
    return n == "0" || ((n | 0) > 0 && n % 1 == 0);
}

DEMO: http://jsfiddle.net/5UCy4/2/


Hãy thử một chuỗi trống hoặc một số lớn, chẳng hạn như 1290000981231123.1 - jsfiddle.net/5UCy4/1
Niko

@gdoron Giống như ~~val, cắt bỏ một phần phân số. Nó nhanh hơn một chút vì thực hiện một thao tác bitwise thay vì hai.
VisioN

Câu trả lời chính xác. Cá nhân tôi thích được tiết lộ hơn một chút về khả năng đọc(!isNaN(parseFloat(n)) && n % 1 === 0 && 0 <= ~~n)
Ramy Nasr

true, false, 0xFFvà các giá trị khác làm cho nó đi qua isPositiveIntegermà không bị phát hiện.
Xeoncross

1
Điều này thậm chí xử lý các số không đệm (tốt!). Bạn có thể thêm nó vào bài kiểm tra của bạn.
Rashack

21

Có vẻ như một biểu thức thông thường là cách để đi:

var isInt = /^\+?\d+$/.test('the string');

Đóng, trừ khi "1.0" hoặc ".1e1" được cho phép.
Phrogz

Vâng 1290000192379182379123782900192981231 là một số nguyên nhưng nó không thể biểu thị chính xác bằng số gốc JavaScript, do đó, với một biểu thức chính, vẫn cần phải thực hiện chuyển đổi số và xác minh rằng nó đã hoạt động.
Mũi nhọn

Đây là một giải pháp rất không chính xác.
usr

@usr: Tôi cho rằng nó cho phép dẫn đến 0nơi mà bạn thường không mong đợi, nhưng đối với hầu hết các phần, có vẻ ổn.
TJ Crowder

Nó không kiểm tra phạm vi tối đa là 2 ^ 31-1 và nó không cho phép các chữ số Ả Rập. Đây là một hack, không phải là một giải pháp tốt. Giải pháp tốt nhất có nhiều upvote nhất trong câu hỏi này. Tại sao không sử dụng cái đó? Nó là tốt hơn trong mọi vấn đề.
usr

17

Giải pháp hiện đại hoạt động trong nút và trên 90% tất cả các trình duyệt (trừ IE và Opera Mini) là sử dụng Number.isInteger theo sau là một kiểm tra tích cực đơn giản.

Number.isInteger(x) && x > 0

Điều này đã được hoàn thiện trong ECMAScript 2015 .

function isPositiveInteger(x) {
    return Number.isInteger(x) && x > 0
}

Polyfil là:

Number.isInteger = Number.isInteger || function(value) {
  return typeof value === 'number' && 
    isFinite(value) && 
    Math.floor(value) === value;
};

Nếu bạn cần hỗ trợ đầu vào có thể ở dạng chuỗi hoặc số thì bạn có thể sử dụng chức năng này Tôi đã viết một bộ kiểm tra lớn sau khi tất cả các câu trả lời hiện có (2/1/2018) không thành công trên một số hình thức đầu vào.

function isPositiveInteger(v) {
  var i;
  return v && (i = parseInt(v)) && i > 0 && (i === v || ''+i === v);
}

4

Đây gần như là một câu hỏi trùng lặp cho câu hỏi này:

Xác thực số thập phân trong JavaScript - IsNumeric ()

Đó là câu trả lời là:

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

vì vậy, một số nguyên dương sẽ là:

function isPositiveInteger(n) {
  var floatN = parseFloat(n);
  return !isNaN(floatN) && isFinite(n) && floatN > 0
      && floatN % 1 == 0;
}

Tôi biết đó gần như là một bản sao cho câu hỏi 'Xác thực số trong JavaScript', tôi đã đọc toàn bộ điều đó, nhưng tôi nghĩ một câu hỏi cụ thể về cách biểu diễn chuỗi số nguyên xứng đáng là trang riêng của nó.
Mick Byrne

2
return ((parseInt(str, 10).toString() == str) && str.indexOf('-') === -1);

sẽ không hoạt động nếu bạn đưa ra một chuỗi như '0001'


2

Hàm của tôi kiểm tra nếu số là + ve và cũng có thể có giá trị thập phân.

       function validateNumeric(numValue){
            var value = parseFloat(numValue);
            if (!numValue.toString().match(/^[-]?\d*\.?\d*$/)) 
                    return false;
            else if (numValue < 0) {
                return false;
            }
            return true;        
        }

2

Chỉ cần xây dựng dựa trên câu trả lời của VisioN ở trên, nếu bạn đang sử dụng plugin xác thực jQuery, bạn có thể sử dụng điều này:

$(document).ready(function() {
    $.validator.addMethod('integer', function(value, element, param) {
        return (value >>> 0 === parseFloat(value) && value > 0);
    }, 'Please enter a non zero integer value!');
}

Sau đó, bạn có thể sử dụng trong bộ quy tắc thông thường của mình hoặc thêm nó một cách linh hoạt theo cách này:

$("#positiveIntegerField").rules("add", {required:true, integer:true});

2

Đơn giản

function isInteger(num) {
  return (num ^ 0) === num;
}

console.log(isInteger(1));

Bạn cũng có thể mở rộng Số và gán chức năng cho nó thông qua nguyên mẫu.


1

Nếu bạn đang sử dụng biểu mẫu HTML5, bạn có thể sử dụng thuộc tính min="0"cho thành phần biểu mẫu <input type="number" />. Điều này được hỗ trợ bởi tất cả các trình duyệt chính. Nó không liên quan đến Javascript cho các tác vụ đơn giản như vậy, nhưng được tích hợp trong tiêu chuẩn html mới. Nó được ghi lại trên https://www.w3schools.com/tags/att_input_min.asp


0

(~~a == a)nơi alà chuỗi.


1
Điều này sẽ thất bại cho số âm: ~~"-1" == "-1"sẽ trở lại true. Và OP chỉ yêu cầu số nguyên dương.
Igor Milla

1
Cũng thất bại cho '' và ''.
neverfox

0

ES6:

Number.isInteger(Number(theNumberString)) > 0
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.