Побитовые операции со строками в JavaScript
в javascript следующий тест символьных двоичных операций печатает 0
676 раз:
var s = 'abcdefghijklmnopqrstuvwxyz';
var i, j;
for(i=0; i<s.length;i++){ for(j=0; j<s.length;j++){ console.log(s[i] | s[j]) }};
если js использовал фактическое двоичное представление строк, я ожидал бы здесь некоторых ненулевых значений.
аналогично, тестирование двоичных операций над строками и целыми числами, следующая печать 26 255
s и 0
s, соответственно. (255 было выбрано, потому что это 11111111
в двоичном).
var s = 'abcdefghijklmnopqrstuvwxyz';
var i; for(i=0; i<s.length;i++){ console.log(s[i] | 255) }
var i; for(i=0; i<s.length;i++){ console.log(s[i] & 255) }
что здесь делает javascript? Похоже, javascript бросает любую строку в false
перед бинарные операции.
Примечания
если вы попробуете это в Python, он выдает ошибку:
>>> s = 'abcdefghijklmnopqrstuvwxyz'
>>> [c1 | c2 for c2 in s for c1 in s]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'str' and 'str'
но такие вещи, как это кажется работа в php.
3 ответов
в JavaScript, когда строка используется с двоичным оператором, она сначала преобразуется в число. Соответствующие части спецификации ECMAScript показаны ниже, чтобы объяснить, как это работает.
производство A : A @ B, где @ - один из побитовых операторов в производствах выше, оценивается следующим образом:
- пусть lref будет результатом оценки A.
- пусть lval будет GetValue (lref).
- пусть rref будет результатом оценки B.
- пусть rval будет GetValue (rref).
- пусть lnum будет ToInt32 (lval).
- пусть rnum будет ToInt32 (rval).
- возвращает результат применения побитового оператора @ к lnum и rnum. В результате получается 32-разрядное целое число со знаком.
абстрактная операция ToInt32 преобразует аргумент к одному из 232 целочисленные значения в диапазоне от -231 до 231-1 включительно. Эта абстрактная операция функционирует следующим образом:
- пусть number является результатом вызова ToNumber во входном аргументе.
- если число NaN, +0, -0,+∞, или -∞, возврат +0.
- пусть posInt-знак (номер) * пол (abs (номер)).
- пусть int32bit будет posInt по модулю 232; то есть, конечное целое значение k Введите число с положительным знаком и менее 232 по величине такой, что математическая разница posInt и k математически является целым числом, кратным 232.
- если int32bit больше или равно 231, возврат int32bit-232, иначе возвращает int32bit.
внутренняя функция ToNumber вернет NaN для любой строки, которая не может быть проанализирована как число, и ToInt32 (NaN) даст 0. Таким образом, в вашем примере кода все побитовые операторы с буквами в качестве операндов будут оцениваться как 0 | 0
, что объясняет, почему печатается только 0.
обратите внимание, что что-то вроде '7' | '8'
будет оценено как 7 | 8
потому что в этом случае строки, используемые в качестве операндов могут быть успешно объединить в число.
что касается того, почему поведение в Python отличается, на самом деле нет никакого неявного преобразования типов в Python, поэтому ожидается ошибка любой тип, который не реализует двоичные операторы (с помощью __or__
, __and__
, etc.), и строки не реализуют эти двоичные операторы.
Perl делает что-то совершенно другое, побитовые операторы реализованы для строк и он будет по существу выполнять побитовый оператор для соответствующих байтов из каждой строки.
если вы хотите использовать JavaScript и получить тот же результат, что и Perl, вам нужно сначала преобразовать символы в их кодовые точки с помощью str.charCodeAt
, выполните побитовый оператор на результирующих целых числах, а затем используйте String.fromCodePoint
для преобразования результирующих числовых значений в символы.
Я был бы удивлен, если бы JavaScript работал вообще с побитовыми операциями над нечисловыми строками и производил что-либо значимое. Я бы предположил, что, поскольку любой побитовый оператор в JavaScript преобразует свой операнд в 32-битное целое число, он просто превратит все нечисловые строки в 0
.
Я бы использовал...
"a".charCodeAt(0) & 0xFF
производит 97
, код ASCII для "a", который является правильным, учитывая, что он замаскирован байтом со всеми битами набор.
постарайтесь запомнить, что, поскольку все хорошо работает на других языках, это не всегда так в JavaScript. Мы говорим о языке, задуманном и реализованном за очень короткое время.
JavaScript использует принуждение типа, которое позволяет ему пытаться автоматически анализировать строки как числа при попытке выполнить над ними числовую операцию. Проанализированное значение равно 0 или более вероятно NaN
. Это, очевидно, не даст вам информацию, которую вы пытаетесь получить.
Я думаю, что вы ищете -charCodeAt
что позволит вам получить числовое значение Unicode для символа в строке и, возможно, дополнительный fromCodePoint
, который преобразует числовое значение символа.