Почему parseInt ('dsff66', 16) возвращает 13?
сегодня я наткнулся на странный (на мой взгляд) случай в JavaScript. Я передал не шестнадцатеричную строку в функцию parseInt с базой 16 и... Я получил результат.
Я ожидал бы, что функция закроет какое-то исключение или, по крайней мере, вернет NaN, но ему удалось разобрать его и вернуть int.
Мой вызов:
var parsed = parseInt('dsff66', 16); // note the 's' in the first argument
document.write(parsed);
Ответы
Ответ 1
Зачем кому-то нужно, чтобы эта функция вела себя так (верните целое число, даже если это не точное представление переданной строки)?
Потому что большую часть времени (безусловно) вы работаете с базовыми номерами 10, и в этом случае JS может просто отличить - не разобрать - строку к числу. (edit: По-видимому, не только base-10, см. обновление ниже.)
Так как JS динамически типизирован, некоторые строки работают отлично, как числа без какой-либо работы с вашей стороны. Например:
"21" / 3; // => 7
"12.4" / 4; // => 3.1
Нет необходимости в parseInt
, потому что "21"
и "12.4"
уже являются по существу числами. Если, однако, строка была "12.4xyz"
, тогда вы действительно получили бы NaN
при делении, так как это явно не число и не может быть неявно брошено или принуждено к одному.
Вы также можете явно "отличить" строку до номера с помощью Number(someString)
. Пока он поддерживает только базу 10,, он действительно вернет NaN
для недопустимых строк.
Так как JS уже имеет неявный и явный тип casting/conversion/coercion, роль parseInt
не должна быть еще одной функцией кастинга.
parseInt
роль - это, быть может, функция синтаксического анализа. Функция, которая изо всех сил пытается понять ее вклад, возвращая все, что может. Это, когда у вас есть строка, которую вы не можете просто отличить, потому что она не совсем идеальна. (И, как основной синтаксис JS, он напоминает C, поскольку ответ apsillers объяснялся хорошо.)
И так как это синтаксический анализатор, а не функция литья, он получил дополнительную возможность иметь возможность обрабатывать другие базы, кроме 10.
Теперь вы можете спросить, почему нет строгой функции кастинга, которая обрабатывает номера не-base-10 и будет жаловаться так, как вы хотите, но... эй, там просто нет. Дизайнеры JS просто решили, что parseInt
будет достаточно, потому что, опять же, 0x63 процента времени, вы имеете дело с базой 10.
Ближе всего вы можете перейти к "кастингу", вероятно, что-то ужасно хакерское:
var hexString = "dsff66";
var number = eval("0x" + hexString); // attempt to interpret as a hexadecimal literal
который выкинет SyntaxError
, потому что 0xdsff66
не является допустимым шестнадцатеричным литералом.
Обновление:. Как отмечает Лекенштейн в комментариях, JS, похоже, правильно добавляет шестнадцатеричные строки 0x
-prefixed. Я этого не знал, но, похоже, это работает:
1 * "0xd0ff66"; // => 13696870
1 * "0xdsff66"; // => NaN
что делает его самым простым способом передать шестнадцатеричную строку в число - и получить NaN
, если он не может быть правильно представлен.
Такое же поведение относится к Number()
, например, Number("0xd0ff66")
возвращает целое число, а Number("0xdsff66")
возвращает NaN
.
(/обновление)
В качестве альтернативы вы можете проверить строку заранее и вернуть NaN
при необходимости:
function hexToNumber(string) {
if( !/^(0x)?[0-9a-f]+$/i.test(string) ) return Number.NaN;
return parseInt(string, 16);
}
Ответ 2
parseInt
считывает ввод до тех пор, пока не встретит недопустимый символ, а затем использует любой действительный ввод, который он считывает до этого недопустимого символа. Рассмотрим:
parseInt("17days", 10);
Это будет использовать вход 17
и опустить все после недопустимого d
.
Из спецификация ECMAScript:
Если [входная строка] S содержит любой символ, который не является цифрой radix-R, тогда пусть Z [строка должна быть целочисленной] быть подстрокой в S, состоящей из всех символов перед первым таким символом; в противном случае пусть Z - S.
В вашем примере s
является недопустимым символом base-16, поэтому parseInt
использует только ведущий d
.
Что касается того, почему это поведение было включено: нет никакого способа узнать наверняка, но это, скорее всего, попытка воспроизвести поведение strtol
(строка в длину) из стандартной библиотеки C. На странице strtol(3)
:
... строка преобразуется в значение long int очевидным образом, останавливается при первом символе, который не является допустимой цифрой в данной базе.
Это соединение дополнительно поддерживается (в некоторой степени) тем, что оба параметра parseInt
и strtol
указаны, чтобы игнорировать ведущие пробелы, и они могут принимать первые 0x
для шестнадцатеричных значений.
Ответ 3
В этом конкретном случае parseInt()
интерпретируйте букву от "A"
до "F"
как hexadecimal
и проанализируйте ее до десятичных чисел. Это означает, что d
вернет 13
.
Что делает parseInt()
-
parseInt("string", radix)
интерпретировать числа и буквы в строке как шестнадцатеричные (они зависят от числа оснований) от числа.
-
parseInt()
только номер или буква синтаксического анализа как шестнадцатеричный с начала строки до тех пор, пока недопустимый символ не станет шестнадцатеричным.
-
Если parseInt()
не может найти любое число или букву в качестве шестнадцатеричного в начале строки parseInt()
, вернет NaN.
-
Если радиус не определен, радиус 10
.
-
Если строка начинается с "0x"
, радиус 16
.
-
Если установленный радиус 0
, радиус 10
.
-
Если радиус 1
, parseInt()
возвращает NaN.
-
Если радиус 2
, parseInt()
, выполните синтаксический разбор "0"
и "1"
.
-
Если радиус 3
, parseInt() разобрать только "0"
, "1"
и "2"
. И так далее.
-
parseInt()
parse "0"
до 0
, если в нем нет номера, и удалите 0
, если за ним следует число. например "0" return 0 и "01" return 1.
-
Если радиус 11
, parseInt()
используется только синтаксическая строка, начинающаяся с номера от "0"
до "9"
и/или буквы "A"
.
-
Если радиус 12
, parseInt обрабатывает только строку, начинающуюся с номера от "0"
до "9"
и/или буквы "A"
и "B"
и т.д.
-
максимальный радиус 36
, он будет анализировать строку, начинающуюся с номера от "0"
до "9"
и/или буквы от "A"
до "Z"
.
-
Если символы, интерпретируемые как шестнадцатеричные, более одного, каждый символ будет иметь другое значение, хотя эти символы являются одним и тем же символом. например parseInt("AA", 11)
первый "A"
имеет другое значение со вторым "A"
.
-
Различные radix возвращают другое число, хотя строки - это одна и та же строка.
Посмотрите в действии
document.body.innerHTML = "<b>What parseInt() does</b><br>" +
"parseInt('9') = " + parseInt('9') + "<br>" +
"parseInt('0129ABZ', 0) = " + parseInt('0129ABZ', 0) + "<br>" +
"parseInt('0', 1) = " + parseInt('0', 1) + "<br>" +
"parseInt('0', 2) = " + parseInt('0', 2) + "<br>" +
"parseInt('10', 2) = " + parseInt('10', 2) + "<br>" +
"parseInt('01', 2) = " + parseInt('01', 2) + "<br>" +
"parseInt('1', 2) = " + parseInt('1', 2) + "<br>" +
"parseInt('A', 10) = " + parseInt('A', 10) + "<br>" +
"parseInt('A', 11) = " + parseInt('A', 11) + "<br>" +
"parseInt('Z', 36) = " + parseInt('Z', 36) + "<br><br>" +
"<b>The value:</b><br>" +
"parseInt('A', 11) = " + parseInt('A', 11) + "<br>" +
"parseInt('A', 12) = " + parseInt('A', 12) + "<br>" +
"parseInt('A', 13) = " + parseInt('A', 13) + "<br>" +
"parseInt('AA', 11) = " + parseInt('AA', 11) + " = 100 + 20" + "<br>" +
"parseInt('AA', 12) = " + parseInt('AA', 12) + " = 100 + 30" + "<br>" +
"parseInt('AA', 13) = " + parseInt('AA', 13) + " = 100 + 40" + "<br>" +
"parseInt('AAA', 11) = " + parseInt('AAA', 11) + " = 1000 + 300 + 30" + "<br>" +
"parseInt('AAA', 12) = " + parseInt('AAA', 12) + " = 1000 + 500 + 70" + "<br>" +
"parseInt('AAA', 13) = " + parseInt('AAA', 13) + " = 1000 + 700 + 130" + "<br>" +
"parseInt('AAA', 14) = " + parseInt('AAA', 14) + " = 1000 + 900 + 210" + "<br>" +
"parseInt('AAA', 15) = " + parseInt('AAA', 15) + " = 1000 + 1100 + 310";
Ответ 4
For radices above 10, the letters of the alphabet indicate numerals greater than 9. For example, for hexadecimal numbers (base 16), A through F are used.
В вашей строке dsff66
, d
является шестнадцатеричным символом (даже если строка не является шестнадцатеричной), которая соответствует типу radix и эквивалентна числу 13
. После этого он перестает разбираться, так как следующий символ не является шестнадцатеричным, следовательно, результатом.