Как преобразовать последние 4 байта в массиве в целое число?
Если у меня есть Uint8Array
массив в JavaScript, как бы я получить последние четыре байта, а затем преобразовать это в int? Используя C#, я бы сделал что-то вроде этого:
int count = BitConverter.ToInt32(array, array.Length - 4);
Есть ли неэквивалентный способ сделать это с помощью JavaScript?
8 ответов
Доступ к основному ArrayBuffer
и создать новый TypedArray
с кусочком его байтов:
var u8 = new Uint8Array([1,2,3,4,5,6]); // original array
var u32bytes = u8.buffer.slice(-4); // last four bytes as a new `ArrayBuffer`
var uint = new Uint32Array(u32bytes)[0];
Если TypedArray
не покрывает весь буфер, вам нужно быть немного хитрее, но не намного:
var startbyte = u8.byteOffset + u8.byteLength - Uint32Array.BYTES_PER_ELEMENT;
var u32bytes = u8.buffer.slice(startbyte, startbyte + Uint32Array.BYTES_PER_ELEMENT);
Это работает в обоих случаях.
Если байты, которые вы хотите разместить на границе выравнивания вашего базового буфера для типа данных (например, вам нужно 32-битное значение байтов 4-8 базового буфера), вы можете избежать копирования байтов с помощью slice()
и просто предоставьте байтовое смещение конструктору представления, как в ответе @Bergi.
Ниже приведена очень проверенная функция, которая должна получить скалярное значение любого смещения, которое вы хотите. Это позволит избежать копирования, если это возможно.
function InvalidArgument(msg) {
this.message = msg | null;
}
function scalarValue(buf_or_view, byteOffset, type) {
var buffer, bufslice, view, sliceLength = type.BYTES_PER_ELEMENT;
if (buf_or_view instanceof ArrayBuffer) {
buffer = buf_or_view;
if (byteOffset < 0) {
byteOffset = buffer.byteLength - byteOffset;
}
} else if (buf_or_view.buffer instanceof ArrayBuffer) {
view = buf_or_view;
buffer = view.buffer;
if (byteOffset < 0) {
byteOffset = view.byteOffset + view.byteLength + byteOffset;
} else {
byteOffset = view.byteOffset + byteOffset;
}
return scalarValue(buffer, view.byteOffset + byteOffset, type);
} else {
throw new InvalidArgument('buf_or_view must be ArrayBuffer or have a .buffer property');
}
// assert buffer instanceof ArrayBuffer
// assert byteOffset > 0
// assert byteOffset relative to entire buffer
try {
// try in-place first
// only works if byteOffset % slicelength === 0
return (new type(buffer, byteOffset, 1))[0]
} catch (e) {
// if this doesn't work, we need to copy the bytes (slice them out)
bufslice = buffer.slice(byteOffset, byteOffset + sliceLength);
return (new type(bufslice, 0, 1))[0]
}
}
Вы бы использовали это так:
// positive or negative byte offset
// relative to beginning or end *of a view*
100992003 === scalarValueAs(u8, -4, Uint32Array)
// positive or negative byte offset
// relative to the beginning or end *of a buffer*
100992003 === scalarValue(u8.buffer, -4, Uint32Array)
У вас есть пример? Я думаю, что это сделало бы это:
var result = ((array[array.length - 1]) |
(array[array.length - 2] << 8) |
(array[array.length - 3] << 16) |
(array[array.length - 4] << 24));
В настоящее время, если вы можете жить с IE 11+ / Chrome 49+ / Firefox 50+, то вы можете использовать DataView, чтобы сделать вашу жизнь почти такой же простой, как в C#:
var u8array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]); // -1
var view = new DataView(u8array.buffer)
console.log("result:" + view.getInt32());
Проверьте это здесь: https://jsfiddle.net/3udtek18/1/
Я удивлен, что в других ответах не используется собственный объект Buffer, который предоставляет множество этих инструментов в простой собственной библиотеке. Возможно, эта библиотека не так широко используется для распаковки/распаковки бит просто потому, что люди не думают проверять здесь, и мне тоже потребовалось некоторое время, чтобы найти ее, но это правильный инструмент для распаковки/распаковки битов в nodejs/javascript/ машинопись.
Вы можете использовать его так:
// Create a simple array with 5 elements. We'll pop the last 4 and you should expect the end value to be 1 because this is a little-endian array with all zeros other than the 1 in the littlest(?)-endian
const array = [0, 1, 0, 0, 0]
// get the last 4 elements of your array and convert it to a Buffer
const buffer = Buffer.from(array.slice(-4));
// Use the native Buffer type to read the object as an (U) unsigned (LE) little-endian 32 (32 bits) integer
const value = Buffer.readUInt32LE();
Или, более кратко:
const value = Buffer.from(array.slice(-4)).readUInt32LE();
Немного не элегантно, но если вы можете сделать это вручную на основе порядка байтов.
Little endian:
var count = 0;
// assuming the array has at least four elements
for(var i = array.length - 1; i >= array.length - 4; i--)
{
count = count << 8 + array[i];
}
Big endian:
var count = 0;
// assuming the array has at least four elements
for(var i = array.length - 4; i <= array.length - 1 ; i++)
{
count = count << 8 + array[i];
}
Это может быть расширено на другие длины данных
Редактировать: Спасибо Дэвиду за указание на мои опечатки
Это должно быть более эффективным, просто создать Uint32Array
посмотреть на тот же ArrayBuffer и получить прямой доступ к 32-битному номеру:
var uint8array = new Uint8Array([1,2,3,4,5,6,7,8]);
var uint32array = new Uint32Array(
uint8array.buffer,
uint8array.byteOffset + uint8array.byteLength - 4,
1 // 4Bytes long
);
return uint32array[0];
var a = Uint8Array(6)
a.set([1,2,8,0,0,1])
i1 = a[a.length-4];
i2 = a[a.length-3];
i3 = a[a.length-2];
i4 = a[a.length-1];
console.log(i1<<24 | i2<<16 | i3<<8 | i4);
Обидно, что нет способов сделать это. Мне нужно было читать переменные переменных размеров, поэтому на основе ответа Имортенсона я написал эту маленькую функцию, где p
это позиция чтения и s
количество байтов для чтения:
function readUInt(arr, p, s) {
var r = 0;
for (var i = s-1; i >= 0; i--) {
r |= arr[p + i] << (i * 8);
} return r >>> 0;
}
var iable = readUint(arr, arr.length - 4, 4);