Convalida che una stringa è un numero intero positivo


200

Vorrei che il test fail-safe più semplice verificasse che una stringa in JavaScript sia un numero intero positivo.

isNaN(str)restituisce vero per tutti i tipi di valori non interi e parseInt(str)restituisce numeri interi per stringhe float, come "2.5". E non voglio nemmeno usare alcuni plugin jQuery.


7
Consenti "+2"? Che ne dici di "0.1e1"? Che ne dici di "1.0000"?
Phrogz,

La isNaN()funzione si comporta come fa perché il concetto Not a Numberha un significato molto specifico nella specifica in virgola mobile IEEE 794. Non intende fornire una risposta alla semplice domanda colloquiale, "questo valore non è un numero?"
Punta a punta

3
La domanda è vaga all'estremo. Non è possibile convalidare che "una stringa è un numero intero", perché non esiste una cosa del genere: nessun oggetto può essere una stringa e un numero contemporaneamente. Probabilmente intendevi "come verificare se una stringa è una rappresentazione valida di un numero intero", ma per rispondere a questo dobbiamo sapere di quale lingua o "cultura" stai parlando. Ad esempio, ٢‎٣٤o MCMXIXsono entrambe rappresentazioni di numeri interi validi, ma non credo che tu stia cercando un codice in grado di analizzarli. Potresti specificare quali formati numerici supporterai, poiché le persone sembrano confuse al riguardo.
Georg

5
Penso che "vago all'estremo" sia esso stesso un po 'estremo; ma comunque ... Il contesto è la convalida di un campo di input quantità su un carrello utilizzato in un paese di lingua inglese, quindi solo numeri occidentali. Per "positivo" intendevo maggiore di zero. Accetterei quanto segue: "+2" sì, "0.1e1" no, "1.000" sicuro perché no. Tuttavia, se riesci a fornire una risposta che può essere adattata per includere / escludere questi vari scenari specializzati, ti prometto di darti extra (e sono sicuro che lo faranno anche gli altri).
Mick Byrne,

Risposte:


296

Due risposte per te:

  • Basato sull'analisi

  • Espressione regolare

Si noti che in entrambi i casi ho interpretato "numero intero positivo" da includere 0, anche se 0non è positivo. Includo note se si desidera non consentire0 .

Basato sull'analisi

Se vuoi che sia una stringa intera decimale normalizzata su un intervallo ragionevole di valori, puoi farlo:

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

o se si desidera consentire spazi bianchi e zeri iniziali:

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

Testbed live (senza gestire zeri iniziali o spazi bianchi):

Testbed live ( con gestione per zeri iniziali e spazi bianchi):

Se desideri non consentire 0, >= 0passa a > 0. (Oppure, nella versione che consente zeri iniziali, rimuovere || "0"ilreplace riga.)

Come funziona:

  1. Nella versione che consente spazi bianchi e zeri iniziali:

    • str = str.trim(); rimuove qualsiasi spazio bianco iniziale e finale.
    • if (!str) prende una stringa vuota e ritorna, inutile fare il resto del lavoro.
    • str = str.replace(/^0+/, "") || "0"; rimuove tutti gli 0 iniziali dalla stringa, ma se ciò risulta in una stringa vuota, ripristina un singolo 0.
  2. Number(str): Converti strin un numero; il numero può anche avere una porzione frazionaria o può essere NaN.

  3. Math.floor: Tronca il numero (elimina qualsiasi porzione frazionaria).

  4. String(...): Converte il risultato in una normale stringa decimale. Per numeri davvero grandi, questo andrà alla notazione scientifica, che potrebbe rompere questo approccio. (Non so bene dove sia la divisione, i dettagli sono nelle specifiche , ma per i numeri interi credo che sia nel momento in cui hai superato le 21 cifre [a quel punto il numero è diventato molto impreciso, come IEEE-754 i numeri a doppia precisione hanno solo circa 15 cifre di precisione ..)

  5. ... === str: Confronta quello con la stringa originale.

  6. n >= 0: Verifica che sia positivo.

Si noti che ciò non riesce per l'input "+1", qualsiasi input nella notazione scientifica che non si trasforma nella stessa notazione scientifica nella String(...)fase e per qualsiasi valore che il tipo di numero utilizza JavaScript (punto mobile binario a doppia precisione IEEE-754) non è in grado di rappresentare con precisione quali analisi sono più vicine a un valore diverso da quello indicato (che include molti numeri interi oltre 9.007.199.254.740.992; ad esempio, 1234567890123456789fallirà). Il primo è una soluzione semplice, i secondi due non così tanto.

Espressione regolare

L'altro approccio è testare i caratteri della stringa tramite un'espressione regolare, se il tuo obiettivo è semplicemente consentire (diciamo) un opzionale +seguito da uno 0o da una stringa in normale formato decimale:

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

Testbed live:

Come funziona:

  1. ^: Corrisponde all'inizio della stringa

  2. \+?: Consenti un singolo, facoltativo +(rimuovilo se non vuoi)

  3. (?:...|...): Consenti una di queste due opzioni (senza creare un gruppo di acquisizione):

    1. (0|...): Consenti 0da solo ...

    2. (...|[1-9]\d*): ... o un numero che inizia con qualcosa di diverso da 0e seguito da un numero qualsiasi di cifre decimali.

  4. $: Corrisponde alla fine della stringa.

Se si desidera non consentire 0(perché non è positivo), l'espressione regolare diventa giusta /^\+?[1-9]\d*$/(ad esempio, possiamo perdere l'alternanza che dovevamo consentire 0).

Se si desidera consentire gli zeri iniziali (0123, 00524), sostituire semplicemente l'alternanza (?:0|[1-9]\d*)con\d+

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

Se vuoi consentire spazi bianchi, aggiungi \s*subito dopo ^e \s*subito prima $.

Nota per quando lo converti in un numero: sui motori moderni probabilmente sarebbe bene usarlo +stro Number(str)farlo, ma quelli più vecchi potrebbero estenderli in un modo non standard (ma precedentemente consentito) che dice che uno zero iniziale significa ottale (base 8), ad es. "010" => 8. Dopo aver convalidato il numero, puoi tranquillamente utilizzarlo parseInt(str, 10)per assicurarti che sia analizzato come decimale (base 10). parseIntignorerebbe la spazzatura alla fine della stringa, ma ci siamo assicurati che non ci fosse nessuno con la regex.


entrambi Number(' ')e Number('')return 0while dovrebbe NaNinvece tornare .
neurino,

@TJCrowder ok ma ho bisogno di stringhe come '' 2a '' per essere rifiutate in qualche modo, parseIntritorna 2.
neurino,

@neurino: /\D/.test('2a')è vero (perché c'è una non cifra). Quindi forse if (!/\D/.test(str) && !isNan((num = parseInt(str, 10))) { /* Valid number in num */}... Appena fuori dalla mia testa ...
TJ Crowder

Per quanto riguarda le prestazioni, quale metodo sarebbe più veloce? O quale è consigliabile?
phkavitha,

@phkavitha: la risposta alle domande sulle prestazioni di JavaScript è quasi sempre "Dipende" perché i diversi motori sono così diversi tra loro. Puoi testare i tuoi motori / browser target utilizzando jsperf.com . Non credo che questa operazione potrebbe essere il collo di bottiglia in una data app ... :-)
TJ Crowder,

76

Soluzione 1

Se consideriamo un intero JavaScript come valore massimo 4294967295(ovvero Math.pow(2,32)-1), la seguente soluzione breve funzionerà perfettamente:

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

DESCRIZIONE:

  1. Operatore del cambio destro a riempimento zero fa tre cose importanti:
    • tronca la parte decimale
      • 123.45 >>> 0 === 123
    • fa lo spostamento per i numeri negativi
      • -1 >>> 0 === 4294967295
    • "funziona" nel raggio di MAX_INT
      • 1e10 >>> 0 === 1410065408
      • 1e7 >>> 0 === 10000000
  2. parseFloatcorregge l'analisi dei numeri di stringa (impostazione NaNper stringhe non numeriche)

TEST:

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


Soluzione 2

Un altro modo è valido per tutti i valori numerici che sono validi fino a Number.MAX_VALUE, vale a dire circa 1.7976931348623157e+308:

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

DESCRIZIONE:

  1. !isNaN(parseFloat(n))è usato per filtrare valori di stringa puri , ad es "". " ", "string";
  2. 0 <= ~~nfiltri negativi e grandi valori non interi, ad esempio "-40.1", "129000098131766699";
  3. (!isNaN(parseFloat(n)) && 0 <= ~~n)ritorna truese il valore è sia numerico che positivo ;
  4. 0 === n % (...)controlla se il valore non è float - qui (...)(vedi 3) viene valutato come 0nel caso di false, e come 1nel caso di true.

TEST:

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


La versione precedente:

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

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


Prova una stringa vuota o un numero grande come 1290000981231123.1 - jsfiddle.net/5UCy4/1
Niko

@gdoron Lo stesso di ~~val, tagliando la parte frazionaria. È un po 'più veloce poiché esegue un'operazione bit a bit anziché due.
VisioN,

Bella risposta. Personalmente preferirei essere un po 'più prolisso per la leggibilità(!isNaN(parseFloat(n)) && n % 1 === 0 && 0 <= ~~n)
Ramy Nasr,

true, false, 0xFFe altri valori riescono a passare isPositiveIntegersenza essere rilevati.
Xeoncross

1
Questo gestisce anche zeri imbottiti (bene!). Potresti aggiungerlo ai tuoi test.
Rashack,

21

Sembra che un'espressione regolare sia la strada da percorrere:

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

Chiudi, a meno che non siano consentiti "1.0" o ".1e1".
Phrogz,

Bene 1290000192379182379123782900192981231 è un numero intero ma non è rappresentabile esattamente nei numeri nativi JavaScript, quindi con una regex è ancora necessario fare una conversione numerica e verificarne il funzionamento.
punta

Questa è una soluzione molto imprecisa.
usr

@usr: suppongo che permetta di condurre 0dove normalmente non ti aspetteresti, ma per la maggior parte, sembra a posto.
TJ Crowder,

Non controlla che l'intervallo sia al massimo 2 ^ 31-1 e non consente le cifre arabe. Questo è un trucco, non una buona soluzione. La migliore soluzione ha il maggior numero di voti in questa domanda. Perché non usarlo? È meglio sotto ogni aspetto.
usr

17

La soluzione moderna che funziona su nodo e su oltre il 90% di tutti i browser (tranne IE e Opera Mini) è utilizzare Number.isInteger seguito da un semplice controllo positivo.

Number.isInteger(x) && x > 0

Questo è stato finalizzato in ECMAScript 2015 .

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

Il Polyfil è:

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

Se hai bisogno di supportare input che potrebbero essere in forma di stringa o numero , puoi usare questa funzione per cui ho scritto una grande suite di test dopo che tutte le risposte esistenti (2/1/2018) non sono riuscite su alcune forme di input.

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

4

Questa è quasi una domanda duplicata per questa:

Convalida numeri decimali in JavaScript - IsNumeric ()

La sua risposta è:

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

quindi, un numero intero positivo sarebbe:

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

So che è quasi un duplicato di quella domanda "Convalida numeri in JavaScript", ho letto l'intera cosa, ma ho pensato che una domanda specifica sulle rappresentazioni di stringhe di numeri interi meritasse la sua pagina.
Mick Byrne,

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

non funzionerà se dai una stringa come '0001'


2

La mia funzione controlla se il numero è + ve e potrebbe anche avere un valore decimale.

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

2

Solo per basarsi sulla risposta di VisioN sopra, se si utilizza il plug-in di convalida jQuery è possibile utilizzare questo:

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

Quindi è possibile utilizzare nel normale set di regole o aggiungerlo in modo dinamico in questo modo:

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

2

Semplice

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

console.log(isInteger(1));

Puoi anche estendere Numero e assegnargli la funzione tramite prototipo.


1

Se si utilizzano moduli HTML5, è possibile utilizzare l'attributo min="0"per l'elemento modulo <input type="number" />. Questo è supportato da tutti i principali browser. Non coinvolge Javascript per compiti così semplici, ma è integrato nel nuovo standard html. È documentato su https://www.w3schools.com/tags/att_input_min.asp


0

(~~a == a)dov'è ala stringa.


1
Questo fallirà per i numeri negativi: ~~"-1" == "-1"tornerà true. E OP ha chiesto solo numeri interi positivi.
Igor Milla,

1
Inoltre non riesce per "" e "".
Neverfox

0

ES6:

Number.isInteger(Number(theNumberString)) > 0
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.