How to convert last 4 bytes in an array to an inte

2019-05-01 16:55发布

If I have an Uint8Array array in JavaScript, how would I get the last four bytes and then convert that to an int? Using C# I would do something like this:

int count = BitConverter.ToInt32(array, array.Length - 4);

Is there an inequivalent way to do this using JavaScript?

7条回答
看我几分像从前
2楼-- · 2019-05-01 17:31

Do you have an example? I think this would do it:

var result = ((array[array.length - 1]) | 
              (array[array.length - 2] << 8) | 
              (array[array.length - 3] << 16) | 
              (array[array.length - 4] << 24));
查看更多
Melony?
3楼-- · 2019-05-01 17:32

Access the underlying ArrayBuffer and create a new TypedArray with a slice of its bytes:

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];

If the TypedArray does not cover the entire buffer, you need to be a little trickier, but not much:

var startbyte = u8.byteOffset + u8.byteLength - Uint32Array.BYTES_PER_ELEMENT;
var u32bytes = u8.buffer.slice(startbyte, startbyte + Uint32Array.BYTES_PER_ELEMENT);

This works in both cases.

If the bytes you want fit in the alignment boundary of your underlying buffer for the datatype (e.g., you want the 32-bit value of bytes 4-8 of the underlying buffer), you can avoid copying the bytes with slice() and just supply a byteoffset to the view constructor, as in @Bergi's answer.

Below is a very-lightly-tested function that should get the scalar value of any offset you want. It will avoid copying if possible.

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]
    }
}

You would use it like this:

// 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)
查看更多
地球回转人心会变
4楼-- · 2019-05-01 17:34

It's a shame there are not build in ways to do this. I needed to read variables of variable sizes so based on Imortenson answer I've wrote this little function where p is read position and s is number of bytes to read:

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);
查看更多
再贱就再见
5楼-- · 2019-05-01 17:36

It should be more efficient to just create an Uint32Array view on the same ArrayBuffer and accessing the 32-bit number directly:

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];
查看更多
我命由我不由天
6楼-- · 2019-05-01 17:40

Nowadays if you can live with IE 11+ / Chrome 49+ / Firefox 50+, then you can use DataView to make your life almost as easy as in C#:

var u8array = new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF]); // -1
var view = new DataView(u8array.buffer)
console.log("result:" + view.getInt32());

Test it here: https://jsfiddle.net/3udtek18/1/

查看更多
戒情不戒烟
7楼-- · 2019-05-01 17:41

A little inelegant, but if you can do it manually based on the endianess.

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];
}

This can be extended to other data lengths

Edit: Thanks to David for pointing out my typos

查看更多
登录 后发表回答