Javascript ArrayBuffer转十六进制

Javascript ArrayBuffer to Hex

我有一个Javascript ArrayBuffer,我想将其转换为十六进制字符串。

有人知道我可以调用的功能或已经存在的预先编写的功能吗?

我只能找到用于字符串函数的arraybuffer,但是我想改为使用arraybuffer的hexdump。


1
2
3
4
5
6
7
function buf2hex(buffer) { // buffer is an ArrayBuffer
  return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}

// EXAMPLE:
const buffer = new Uint8Array([ 4, 8, 12, 16 ]).buffer;
console.log(buf2hex(buffer)); // = 04080c10

此功能分为四个步骤:

  • 将缓冲区转换为数组。
  • 对于每个x数组,它将将该元素转换为十六进制字符串(例如,12变为c)。
  • 然后使用该十六进制字符串,并用零左键填充它(例如c变为0c)。
  • 最后,它接受所有十六进制值并将它们连接到单个字符串中。
  • 下面是另一个更长的实现,虽然有点容易理解,但本质上是相同的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    function buf2hex(buffer) { // buffer is an ArrayBuffer
      // create a byte array (Uint8Array) that we can use to read the array buffer
      const byteArray = new Uint8Array(buffer);
     
      // for each element, we want to get its two-digit hexadecimal representation
      const hexParts = [];
      for(let i = 0; i < byteArray.length; i++) {
        // convert value to hexadecimal
        const hex = byteArray[i].toString(16);
       
        // pad with zeros to length 2
        const paddedHex = ('00' + hex).slice(-2);
       
        // push to array
        hexParts.push(paddedHex);
      }
     
      // join all the hex values of the elements into a single string
      return hexParts.join('');
    }

    // EXAMPLE:
    const buffer = new Uint8Array([ 4, 8, 12, 16 ]).buffer;
    console.log(buf2hex(buffer)); // = 04080c10


    这是一个不错的ES6解决方案,它使用padStart并避免了令人困惑的基于原型调用的已接受答案的解决方案。实际上,它也更快。

    1
    2
    3
    4
    5
    6
    function bufferToHex (buffer) {
        return Array
            .from (new Uint8Array (buffer))
            .map (b => b.toString (16).padStart (2,"0"))
            .join ("");
    }

    工作原理:

  • 从保存缓冲区数据的Uint8Array创建Array。这样一来,我们可以在以后修改数组以容纳字符串值。
  • 所有Array项都映射到其十六进制代码,并用0字符填充。
  • 该数组被连接成一个完整的字符串。

  • 这是另一个解决方案,在Chrome(可能还有节点)上,其速度比使用maptoString的其他建议快3倍:

    1
    2
    3
    4
    5
    function bufferToHex(buffer) {
        var s = '', h = '0123456789ABCDEF';
        (new Uint8Array(buffer)).forEach((v) => { s += h[v >> 4] + h[v & 15]; });
        return s;
    }

    额外的好处:您可以轻松选择大写/小写输出。

    在这里查看工作台:http://jsben.ch/Vjx2V


    以下是按速度顺序将ArrayBuffer编码为十六进制的几种方法。最初,所有方法都在Firefox中进行了测试,但后来我去了Chrome(V8)中进行了测试。在Chrome浏览器中,这些方法大体上是相同的顺序,但确实存在些微差异-重要的是,#1是所有环境中最快的方法,而且优势很大。

    如果要查看当前选定答案的速度,可以继续并滚动到此列表的底部。

    注意复制粘贴

    做个好男孩/女孩,并使用解决方案1。它是最快且受支持最好的。在浏览器中唯一快速编码为十六进制的方法是编写优化的C代码并编译为Web Assembly。

    1.预计算的带有for循环的十六进制八位字节(最快/基准)

    此方法为无符号字节的每个可能值([0, 255])计算2个字符的十六进制八位字节,然后仅通过八位字节字符串数组映射ArrayBuffer中的每个值。使用此方法将原始答案归功于Aaron Watters。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const byteToHex = [];

    for (let n = 0; n <= 0xff; ++n)
    {
        const hexOctet = n.toString(16).padStart(2,"0");
        byteToHex.push(hexOctet);
    }

    function hex(arrayBuffer)
    {
        const buff = new Uint8Array(arrayBuffer);
        const hexOctets = []; // new Array(buff.length) is even faster (preallocates necessary array size), then use hexOctets[i] instead of .push()

        for (let i = 0; i < buff.length; ++i)
            hexOctets.push(byteToHex[buff[i]]);

        return hexOctets.join("");
    }

    2.带有Array.map的预计算十六进制八位字节(慢30%)

    与上述方法相同,在该方法中,我们预先计算了一个数组,其中每个索引的值都是该索引值的十六进制字符串,但是我们使用了hack方法,在该方法中,我们使用缓冲区调用了Array原型的map()方法。这是一种更具功能性的方法,但是如果您真的想提高速度,那么您将始终使用for循环而不是ES6数组方法,因为所有现代JS引擎都会更好地优化它们。

    IMPORTANT: You cannot use new Uint8Array(arrayBuffer).map(...). Although Uint8Array implements the ArrayLike interface, its map method will return another Uint8Array which cannot contain strings (hex octets in our case), hence the Array prototype hack.

    1
    2
    3
    4
    5
    6
    7
    function hex(arrayBuffer)
    {
        return Array.prototype.map.call(
            new Uint8Array(arrayBuffer),
            n => byteToHex[n]
        ).join("");
    }

    3.预先计算的ASCII字符代码(慢230%)

    好吧,这是一个令人失望的实验。我写了这个函数,是因为我认为它会比Aaron预先计算的十六进制八位字节还要快-男孩是我错了,哈哈。虽然Aaron将整个字节映射到其对应的2个字符的十六进制代码,但此解决方案使用移位操作来获取每个字节中前4位的十六进制字符,然后获取最后4位的十六进制字符,并使用String.fromCharCode()。坦率地说,我认为String.fromCharCode()的优化应该做得不好,因为它没有被很多人使用,而且在浏览器供应商的优先级列表中也很低。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const asciiCodes = new Uint8Array(
        Array.prototype.map.call(
           "0123456789abcdef",
            char => char.charCodeAt()
        )
    );

    function hex(arrayBuffer)
    {
        const buff = new Uint8Array(buff);
        const charCodes = new Uint8Array(buff.length * 2);

        for (let i = 0; i < buff.length; ++i)
        {
            charCodes[i * 2] = asciiCodes[buff[i] >>> 4];
            charCodes[i * 2 + 1] = asciiCodes[buff[i] & 0xf];
        }

        return String.fromCharCode(...charCodes);
    }

    4. Array.prototype.map() w / padStart()(慢290%)

    此方法使用Number.toString()方法映射字节数组以获取十六进制,然后根据需要通过String.padStart()方法将八位字节填充为" 0"。

    IMPORTANT: String.padStart() is a relative new standard, so you should not use this or method #5 if you are planning on supporting browsers older than 2017 or so or Internet Explorer. TBH if your users are still using IE you should probably just go to their houses at this point and install Chrome/Firefox. Do us all a favor. :^D

    1
    2
    3
    4
    5
    6
    7
    function hex(arrayBuffer)
    {
        return Array.prototype.map.call(
            new Uint8Array(arrayBuffer),
            n => n.toString(16).padStart(2,"0")
        ).join("");
    }

    5. Array.from().map() w / padStart()(慢370%)

    这与#4相同,但不是Array原型hack,而是从Uint8Array创建实际的数字数组,并直接在其上调用map()。我们虽然付出了速度。

    1
    2
    3
    4
    5
    6
    function hex(arrayBuffer)
    {
        return Array.from(new Uint8Array(arrayBuffer))
            .map(n => n.toString(16).padStart(2,"0"))
            .join("");
    }

    6. Array.prototype.map() w / slice()(慢450%)

    这是选定的答案,除非您是典型的Web开发人员,并且性能使您感到不安,否则请不要使用它(答案#1受许多浏览器支持)。

    1
    2
    3
    4
    5
    6
    7
    function hex(arrayBuffer)
    {
        return Array.prototype.map.call(
            new Uint8Array(arrayBuffer),
            n => ("0" + n.toString(16)).slice(-2)
        ).join("");
    }

    以下解决方案使用预先计算的查找表进行正向和反向转换。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // look up tables
    var to_hex_array = [];
    var to_byte_map = {};
    for (var ord=0; ord<=0xff; ord++) {
        var s = ord.toString(16);
        if (s.length < 2) {
            s ="0" + s;
        }
        to_hex_array.push(s);
        to_byte_map[s] = ord;
    }

    // converter using lookups
    function bufferToHex2(buffer) {
        var hex_array = [];
        //(new Uint8Array(buffer)).forEach((v) => { hex_array.push(to_hex_array[v]) });
        for (var i=0; i<buffer.length; i++) {
            hex_array.push(to_hex_array[buffer[i]]);
        }
        return hex_array.join('')
    }
    // reverse conversion using lookups
    function hexToBuffer(s) {
        var length2 = s.length;
        if ((length2 % 2) != 0) {
            throw"hex string must have length a multiple of 2";
        }
        var length = length2 / 2;
        var result = new Uint8Array(length);
        for (var i=0; i<length; i++) {
            var i2 = i * 2;
            var b = s.substring(i2, i2 + 2);
            result[i] = to_byte_map[b];
        }
        return result;
    }

    此解决方案比先前基准测试的获胜者更快:
    http://jsben.ch/owCk5已在Mac笔记本电脑上的Chrome和Firefox中进行了测试。另请参阅测试验证功能的基准代码。

    [编辑:我将forEach更改为for循环,现在它更快了。


    我用它来六角转储ArrayBuffer,就像Node转储Buffer一样。

    1
    2
    3
    4
    5
    6
    7
    8
    function pad(n: string, width: number, z = '0') {
        return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
    }
    function hexdump(buf: ArrayBuffer) {
        let view = new Uint8Array(buf);
        let hex = Array.from(view).map(v => this.pad(v.toString(16), 2));
        return `<Buffer ${hex.join("")}>`;
    }

    示例(带有转译的js版本):

    1
    2
    const buffer = new Uint8Array([ 4, 8, 12, 16 ]).buffer;
    console.log(hexdump(buffer)); // <Buffer 04 08 0c 10>