Come aggirare il comportamento dell'analisi ottale di JavaScript?


283

Prova a eseguire quanto segue in JavaScript:

parseInt('01'); //equals 1
parseInt('02'); //equals 2
parseInt('03'); //equals 3
parseInt('04'); //equals 4
parseInt('05'); //equals 5
parseInt('06'); //equals 6
parseInt('07'); //equals 7
parseInt('08'); //equals 0 !!
parseInt('09'); //equals 0 !!

Ho appena imparato a mie spese che JavaScript pensa che lo zero iniziale indichi un numero intero ottale , e poiché non esiste "8"o "9"in base-8, la funzione restituisce zero. Piaccia o no, questo è di progettazione .

Quali sono le soluzioni alternative?

Nota: per completezza, sto per pubblicare una soluzione, ma è una soluzione che odio, quindi per favore pubblica altre / risposte migliori.


Aggiornare:

La 5a edizione dello standard JavaScript ( ECMA-262 ) introduce una modifica rivoluzionaria che elimina questo comportamento. Mozilla ha una buona scrittura .


21
Step 1) Fatti un favore e includi sempre il radix come menzionato nelle risposte precedenti e nel libro di Doug. Passaggio 2) Se vuoi davvero imparare JavaScript, procurati una copia del libro di Doug. È inestimabile. Il mio libro preferito finora. Ecco una recensione fyi: realtech.burningbird.net/learning-javascript/basics/…
fuentesjr,

8
Nei browser compatibili con ECMAScript 5th Edition, come Internet Explorer 9, il parametro di base è impostato su 10(decimale) a meno che non sia prefissato il numero da analizzare 0x, ad esempio 0xFF, nel qual caso il parametro di base è impostato su 16. Si spera, un giorno, questo problema sarà un lontano ricordo.
Andy E

2
Che ne dici di solo +'08' === 8? Vero! Forse hai davvero bisogno parseIntdel tuo vero codice, ma non di quello sopra.
Kojiro,

1
a) questo non è un bug, correggi il titolo b)Number('08')
OnTheFly

4
@portman: "la 5a edizione ... introduce un cambiamento radicale che elimina questo comportamento" Probabilmente vale la pena sottolineare che anche nella 3a edizione (13 anni fa), le implementazioni sono state "incoraggiate" a non farlo: "Quando radix è 0o undefinede il numero della stringa inizia con una 0cifra non seguita da un xo X, quindi l'implementazione può, a sua discrezione, interpretare il numero come ottale o come decimale. Le implementazioni sono incoraggiate a interpretare i numeri in questo caso come decimali. " ( la mia enfasi)
TJ Crowder

Risposte:


329

Questo è un gotcha Javascript comune con una soluzione semplice:

Basta specificare la base , o 'radix', in questo modo:

parseInt('08',10); // 8

Puoi anche usare il numero :

Number('08'); // 8

57
Numero . Glorioso.
Portman,

10
Il numero ha bisogno di virgolette intorno allo 08. Inoltre, basta essere avvisato, il numero ('08 .123 ') produrrà 8.123 come output. Se vuoi davvero un numero intero, non usare Numero (o abbina il modello all'input per garantire solo numeri interi).
Jason S,

3
Number (08); mi dà 8 in Firefox e IE.
Paolo Bergantino,

3
non fa parte dello standard ECMAscript. Sto testando su JSDB che utilizza Spidermonkey 1.7 (= motore Firefox JS) e mi lamento che "08 non è una costante ottale ECMA-262"
Jason S,

5
Tuttavia, usa '08' tra virgolette. 08 non soddisfa lo standard ECMA-262 e non è garantito il successo senza avvisi e / o errori e / o un comportamento specifico specificato.
Jason S,

43

Se sai che il tuo valore sarà compreso nell'intervallo di 32 bit con segno, quindi ~~xfarà la cosa giusta in tutti gli scenari.

~~"08" === 8
~~"foobar" === 0
~~(1.99) === 1
~~(-1.99)  === -1

Se cerchi binary not ( ~), la specifica richiede una conversione "ToInt32" per l'argomento che esegue la conversione ovvia in un Int32 e viene specificato per forzareNaN valori a zero.

Sì, questo è incredibilmente hackish ma è così conveniente ...


2
Perché due operazioni quando una binaria o sarebbe sufficiente?
Oleg V. Volkov,

@Oleg, Perché uno dovrebbe bastare?
Sebastian

3
@SebastianGodelet, | 0.
Oleg V. Volkov,

@Grodriguez, e quando 0 in | 0 è una stringa?
Oleg V. Volkov,

Tutta questa domanda riguarda le alternative all'analisi per convertire le stringhe in numeri interi. Quindi: sempre.
Grodriguez,

24

Dalla documentazione parseInt , utilizzare l'argomento radix opzionale per specificare base-10:

parseInt('08', 10); //equals 8
parseInt('09', 10); //equals 9

Questo mi sembra pedante, confuso e prolisso (davvero, un argomento in più in ogni singolo test?), Quindi spero che ci sia un modo migliore.


6
se non ti piace la verbosità, crea la tua funzione che chiama la funzione incorporata e riempie gli argomenti che mantieni sempre costanti.
Jason S,

3
In verità, perché non è come se ogni singola persona su StackOverflow ti rimproverasse per aver scritto la funzione parseIntB10. Scrivere la propria funzione wrapper è un'idea terribile per questo scopo.
Stefan Kendall,

2
@iftrue: penso che ti sia sfuggito il punto. Personalmente non mi dispiace solo fare parseInt (someString, 10) ovunque per assicurarmi di forzare la base 10. L'OP sembra non gradire questo approccio, quindi ho suggerito un'alternativa, che non avrei usato personalmente ma che forse incontra il suo esigenze. (questo è apparentemente il pensiero dietro JQuery: renderlo conveniente aggiungendo ulteriore complessità. Non uso JQuery ma molte persone lo trovano utile.)
Jason S

4
In realtà è molto importante eseguire il wrapping parseIntper un comodo utilizzo nelle espressioni funzionali, come mapin ["7","4","09","5"].map(parseInt); Mappasserà l' indice dell'elemento nell'array come secondo argomento, che verrà interpretato parseIntcome base se non lo si avvolge.
Plynx,

11
function parseDecimal(s) { return parseInt(s, 10); }

modifica: creare la propria funzione, fare ciò che si vuole veramente, è solo un'opzione se non ti piace aggiungere sempre ", 10" alla chiamata parseInt (). Ha lo svantaggio di essere una funzione non standard: più conveniente per te se la usi molto, ma forse più confusa per gli altri.


10

Specifica la base:

var number = parseInt(s, 10);

Wow ragazzi siete veloci. Ho persino avuto la mia risposta negli appunti. Sei collegato direttamente a Internet, in stile neo?
Portman,

1
Perché il diavolo non è impostato sulla base 10
Tom Gullen il

2
Forse perché non è principalmente inteso come una funzione di conversione String-> Number, ma una funzione che legge un numero da una stringa di numero b di base.
artistoex,

2
viene impostato automaticamente sulla base 10 ma gli zeri iniziali sono un modo diffuso per indicare l'ottale.
Nathan,

7

Sarebbe molto cattivo sostituire parseInt con una versione che assume decimali se non ha un secondo parametro? (nota - non testato)

parseIntImpl = parseInt
parseInt = function(str, base){return parseIntImpl(str, base ? base : 10)}

2
sì, sarebbe dannoso - romperebbe altri codici che si basavano sul comportamento standard.
jes5199,

4
Questo è vero, ma non c'è molto. Inoltre, non è un comportamento standard: il supporto ottale è facoltativo.
Andrew Duffy,

2
Ma stai anche lasciando cadere il supporto esadecimale che non è opzionale, vero? Questo dovrebbe essere sempre vero parseInt("0xFFFFFF") === 16777215:, ma con il tuo trucco cattivo in atto non funziona piùparseInt("0xFFFFFF") === 0
Timo


6

È inoltre possibile, invece di utilizzare parseFloat o parseInt, utilizzare l'operatore unario ( + ).

+"01"
// => 1

+"02"
// => 2

+"03"
// => 3

+"04"
// => 4

+"05"
// => 5

+"06"
// => 6

+"07"
// => 7

+"08"
// => 8

+"09"
// => 9

e per buona misura

+"09.09"
// => 9.09

Collegamento MDN

L'operatore unario positivo precede il suo operando e restituisce il suo operando ma tenta di convertirlo in un numero, se non lo è già. Anche se la negazione unaria (-) può anche convertire i non numeri, il plus unario è il modo più rapido e preferito di convertire qualcosa in un numero , perché non esegue altre operazioni sul numero.


5

Che ne dici di questo per i decimali:

('09'-0) === 9  // true

('009'-0) === 9 // true

4

Se hai già fatto un sacco di codice con parseInt e non vuoi aggiungere ", 10" a tutto, puoi semplicemente sovrascrivere la funzione per rendere base 10 l'impostazione predefinita:

window._oldParseInt = window.parseInt;
window.parseInt = function(str, rad) {
    if (! rad) {
        return _oldParseInt(str, 10);
    }
    return _oldParseInt(str, rad);
};

Ciò potrebbe confondere un lettore successivo, quindi creare una funzione parseInt10 () potrebbe essere più autoesplicativo. Personalmente preferisco usare una semplice funzione piuttosto che dover aggiungere ", 10" per tutto il tempo - crea solo più opportunità di errori.


0

Questo problema non può essere replicato nell'ultimo Chrome o Firefox (2019).

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.