Jak obejść zachowanie ósemkowe parsowania JavaScript?

282

Spróbuj wykonać następujące czynności w 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 !!

Właśnie dowiedziałem się na własnej skórze, że JavaScript uważa, że zera wskazuje ósemkowy całkowitą , a ponieważ nie jest "8"ani "9"w bazie-8, funkcja zwraca zero. Czy ci się to podoba, czy nie, jest to zgodne z projektem .

Jakie są obejścia?

Uwaga: Ze względu na kompletność, zamierzam opublikować rozwiązanie, ale nienawidzę tego rozwiązania, więc proszę o publikowanie innych / lepszych odpowiedzi.


Aktualizacja:

Piąta edycja standardu JavaScript ( ECMA-262 ) wprowadza przełomową zmianę, która eliminuje to zachowanie. Mozilla ma dobre write-up .

Portman
źródło
21
Krok 1) Zrób sobie przysługę i zawsze dołączaj podstawkę, jak wspomniano w poprzednich odpowiedziach, a także w książce Douga. Krok 2) Jeśli poważnie myślisz o nauce JavaScript, to zdobądź kopię książki Douga. To jest nieocenione. Moja ulubiona książka do tej pory. Oto recenzja fyi
fuentesjr
8
W przeglądarkach zgodnych z ECMAScript 5th Edition, takich jak Internet Explorer 9, parametr podstawowy ma wartość domyślną 10(dziesiętną), chyba że przed analizowanym numerem jest poprzedzony 0xnp. 0xFFW takim przypadku parametr podstawowy ma wartość 16. Mam nadzieję, że pewnego dnia ten problem będzie odległym wspomnieniem.
Andy E,
2
A może po prostu +'08' === 8? Prawdziwe! Może naprawdę potrzebujesz parseIntprawdziwego kodu, ale nie powyższego.
kojiro
1
a) to nie jest błąd, napraw tytuł b)Number('08')
OnTheFly
4
@portman: „Piąta edycja ... wprowadza przełomową zmianę, która eliminuje to zachowanie” Prawdopodobnie warto zauważyć, że nawet w trzeciej edycji (13 lat temu) implementacje były „zachęcane”, aby tego nie robić: „Gdy podstawa jest 0lub undefineda liczba łańcucha rozpoczyna się 0cyfrą, po której nie następuje znak „ xlub” X, a następnie implementacja może, według własnego uznania, interpretować liczbę jako ósemkową lub dziesiętną. Zachęca się implementacje do interpretowania liczb w tym przypadku jako dziesiętnych. ( mój nacisk)
TJ Crowder

Odpowiedzi:

328

Jest to typowa gotcha JavaScript z prostym rozwiązaniem:

Po prostu określ podstawę lub „radix”, tak jak poniżej:

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

Możesz także użyć numeru :

Number('08'); // 8
Paolo Bergantino
źródło
57
Liczba . Wspaniały.
Portman
10
Liczba potrzebuje cudzysłowów wokół 08. Również ostrzegamy, że Number ('08 .123 ') wygeneruje 8.123 jako wynik. Jeśli naprawdę chcesz liczb całkowitych, nie używaj liczby (lub dopasuj wzorcem dane wejściowe, aby zapewnić tylko liczby całkowite).
Jason S,
3
Numer (08); daje mi 8 w Firefox i IE.
Paolo Bergantino,
3
nie jest częścią standardu ECMAscript. Testuję na JSDB, który używa Spidermonkey 1.7 (= silnik JS Firefoksa) i narzekam, że „08 nie jest legalną stałą ósemkową ECMA-262”
Jason S
5
Nadal jednak używaj „08” w cudzysłowie. 08 nie spełnia normy ECMA-262 i nie ma gwarancji, że odniesie sukces bez ostrzeżeń i / lub błędów i / lub określonego zachowania.
Jason S,
43

Jeśli wiesz , że twoja wartość będzie w zakresie 32-bitowych liczb całkowitych ze znakiem, ~~xzrobi to poprawnie we wszystkich scenariuszach.

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

Jeśli spojrzysz na binarną not ( ~), specyfikacja wymaga konwersji „ToInt32” dla argumentu, który dokonuje oczywistej konwersji na Int32 i jest określony w celu wymuszenia NaNwartości na zero.

Tak, to jest niesamowicie hackerskie, ale jest tak wygodne ...

Karl Guertin
źródło
2
Po co dwie operacje, gdy jedna binarna lub wystarcza?
Oleg V. Volkov
@Oleg, dlaczego wystarczyłoby?
Sebastian
3
@SebastianGodelet, | 0.
Oleg V. Volkov
@Grodriguez, a kiedy 0 w | 0 jest ciągiem?
Oleg V. Volkov
Całe to pytanie dotyczy alternatyw dla parsowania dla konwersji ciągów znaków na liczby całkowite. Zatem: zawsze.
Grodriguez
24

W dokumentacji parseInt użyj opcjonalnego argumentu Radix, aby określić base-10:

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

Wydaje mi się to pedantyczne, mylące i pełne słów (tak naprawdę dodatkowy argument w każdej analizie?), Więc mam nadzieję, że istnieje lepszy sposób.

Portman
źródło
6
jeśli nie lubisz gadatliwości, po prostu stwórz własną funkcję, która wywołuje funkcję wbudowaną i wypełnia argumenty, które zawsze zachowujesz na stałym poziomie.
Jason S,
3
Riiiiiiight, ponieważ to nie jest tak, że każda osoba na StackOverflow skrytykuje cię za pisanie funkcji parseIntB10. Pisanie własnej funkcji otoki jest strasznym pomysłem do tego celu.
Stefan Kendall
2
@iftrue: Myślę, że przegapiłeś mój punkt. Osobiście nie mam nic przeciwko po prostu parsowaniu (someString, 10) wszędzie, aby upewnić się, że wymuszam bazę 10. OP wydaje się nie lubić tego podejścia, więc zasugerowałem alternatywę, z której osobiście nie skorzystałbym, ale być może spełnia jego wymagania. (najwyraźniej takie myślenie kryje się za JQuery: uczyń go wygodniejszym, dodając dodatkową złożoność. Nie używam JQuery, ale wiele osób uważa to za przydatne.)
Jason S
4
W rzeczywistości bardzo ważne jest zawijanie parseIntdla wygodnego użycia w wyrażeniach funkcjonalnych, tak jak mapin ["7","4","09","5"].map(parseInt); Mapprzekazuje indeks elementu w tablicy jako drugi argument, który będzie interpretowany parseIntjako podstawa, chyba że go zawiniesz.
Plynx
11
function parseDecimal(s) { return parseInt(s, 10); }

edit: tworzenie własnej funkcji, aby robić to, czego naprawdę chcesz, to tylko opcja, jeśli nie lubisz dodawać „, 10” przez cały czas do wywołania parseInt (). Ma tę wadę, że jest niestandardową funkcją: wygodniejszą dla Ciebie, jeśli często z niej korzystasz, ale być może bardziej mylącą dla innych.

Jason S.
źródło
10

Określ podstawę:

var number = parseInt(s, 10);
RichieHindle
źródło
Wow, jesteście szybcy. Miałem nawet swoją odpowiedź w schowku. Czy jesteś podłączony bezpośrednio do Internetu, w stylu neo?
Portman
1
Dlaczego diabeł nie jest domyślnie ustawiony na podstawie 10
Tom Gullen
2
Może dlatego, że nie jest to przede wszystkim funkcja konwersji ciągu> Liczby, ale funkcja, która odczytuje liczbę z podstawowego ciągu liczby b.
artistoex,
2
domyślnie jest to podstawa 10, ale zera wiodące są powszechnym sposobem oznaczania liczby ósemkowej.
Nathan
7

Czy bardzo niegrzeczne byłoby zastąpienie parseInt wersją, która przyjmuje liczbę dziesiętną, jeśli nie ma drugiego parametru? (uwaga - nie testowano)

parseIntImpl = parseInt
parseInt = function(str, base){return parseIntImpl(str, base ? base : 10)}
Andrew Duffy
źródło
2
tak, to byłoby niegrzeczne - złamałoby to inny kod, który polegał na standardowym zachowaniu.
jes5199
4
To prawda, ale nie ma tego wiele. To także nie jest standardowe zachowanie - obsługa ósemkowa jest opcjonalna.
Andrew Duffy,
2
Ale upuszczasz także obsługę heksów, co nie jest opcjonalne, prawda? To zawsze powinno być prawdą: parseInt("0xFFFFFF") === 16777215ale z twoim niegrzecznym włamaniem nie działa jużparseInt("0xFFFFFF") === 0
Timo,
6

Możesz także zamiast operatora parseFloat lub parseInt użyć operatora jednoargumentowego ( + ).

+"01"
// => 1

+"02"
// => 2

+"03"
// => 3

+"04"
// => 4

+"05"
// => 5

+"06"
// => 6

+"07"
// => 7

+"08"
// => 8

+"09"
// => 9

i na wszelki wypadek

+"09.09"
// => 9.09

Link MDN

Jednoargumentowy operator plus poprzedza swój operand i analizuje go, ale próbuje przekonwertować go na liczbę, jeśli jeszcze nie jest. Chociaż jednoargumentowa negacja (-) może również konwertować liczby nieparzyste , jednoargumentowy plus jest najszybszym i preferowanym sposobem konwersji czegoś na liczbę , ponieważ nie wykonuje żadnych innych operacji na liczbie.

SoEzPz
źródło
5

Co powiesz na dziesiętny:

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

('009'-0) === 9 // true
Gordon K.
źródło
4

Jeśli wykonałeś już sporo kodowania za pomocą parseInt i nie chcesz dodawać „, 10” do wszystkiego, możesz po prostu przesłonić funkcję, aby ustawić domyślną bazę 10:

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

To może mylić późniejszego czytnika, więc wykonanie funkcji parseInt10 () może być bardziej zrozumiałe. Osobiście wolę używać prostej funkcji niż ciągłe dodawanie „, 10” - po prostu stwarza więcej okazji do błędów.

składnik_15939
źródło
0

Tego problemu nie można replikować w najnowszym Chrome ani Firefox (2019).

Andrija
źródło