【问题标题】:Byte array to Hex string conversion in javascriptjavascript中的字节数组到十六进制字符串的转换
【发布时间】:2016-03-22 11:15:47
【问题描述】:

我有一个[4,-101,122,-41,-30,23,-28,3,..] 形式的字节数组,我想将其转换为6d69f597b217fa333246c2c8 形式 我正在使用以下功能

function toHexString(bytes) {
  return bytes.map(function(byte) {
    return (byte & 0xFF).toString(16)
  }).join('')
}

这给了我一个相同形式的字符串,但我怀疑这不是一种有效的转换,因为十六进制字符串比预期的要短一些。我认为翻译应该得到“0a10a6dc”。 请告诉我我是否错了,或者这是一个正确的转换,但也许我没有使用正确的字节数组

字节数组4,-127,45,126,58,-104,41,-27,-43,27,-35,100,-50,-77,93,-16,96,105,-101,-63,48,-105,49,-67,110,111,26,84,67,-89,-7,-50,10,-12,56,47,-49,-42,-11,-8,-96,-117,-78,97,-105,9,-62,-44,-97,-73,113,96,23,112,-14,-62,103,-104,90,-14,117,78,31,-116,-7

对应转化4812d7e3a9829e5d51bdd64ceb35df060699bc1309731bd6e6f1a5443a7f9ceaf4382fcfd6f5f8a08bb261979c2d49fb771601770f2c267985af2754e1f8cf9

【问题讨论】:

  • 对不起,我已经更新了代码。我在发布之前更改了变量,但现在我使用的是原始代码

标签: javascript arrays bitcoin data-conversion


【解决方案1】:

这是 ArrayBuffer 的跨浏览器解决方案:

    function buf2hex(buffer) {
        var u = new Uint8Array(buffer),
            a = new Array(u.length),
            i = u.length;
        while (i--) // map to hex
            a[i] = (u[i] < 16 ? '0' : '') + u[i].toString(16);
        u = null; // free memory
        return a.join('');
    };

【讨论】:

    【解决方案2】:

    在将字节数组转换为十六进制数组时,我们必须考虑它们如何成为有符号数。如果是这样,我们必须先将它们转换为十进制数。 signed numbers to decimal conversion。然后,我们可以使用.toString(16)方法将其转换为十六进制。

    const hexArr = byteArr.map((byte) => {
        if (byte < 0) {
          byte = -((byte ^ 0xff) + 1); //converting 2s complement to a decimal number
        }
        //add padding at the start to ensure it's always 2 characters long otherwise '01' will be '1'
        return byte.toString(16).padStart(2, '0'); 
    });
    

    【讨论】:

      【解决方案3】:

      所有以前的解决方案都有效,但它们都需要创建许多字符串以及对创建的字符串进行连接和切片。我想既然有类型数组,就必须有更好的方法来解决它。我最初是使用 node 进行此操作,然后将使用 Buffer 的行注释掉并将它们更改为 TypedArrays,以便它也可以在浏览器中工作。

      它的代码更多,但速度明显更快,至少在我整理的快速jsperf 中。接受答案中的字符串操作版本执行 37000 ops/sec,而下面的代码管理 317000 ops/sec。创建字符串对象有很多隐藏的开销。

      function toHexString (byteArray) {
        //const chars = new Buffer(byteArray.length * 2);
        const chars = new Uint8Array(byteArray.length * 2);
        const alpha = 'a'.charCodeAt(0) - 10;
        const digit = '0'.charCodeAt(0);
      
        let p = 0;
        for (let i = 0; i < byteArray.length; i++) {
            let nibble = byteArray[i] >>> 4;
            chars[p++] = nibble > 9 ? nibble + alpha : nibble + digit;
            nibble = byteArray[i] & 0xF;
            chars[p++] = nibble > 9 ? nibble + alpha : nibble + digit;    
        }
      
        //return chars.toString('utf8');
        return String.fromCharCode.apply(null, chars);
      }
      

      【讨论】:

      • 几乎所有现代浏览器都支持类型化数组(甚至 IE 10 和 11)。
      • 如果输入数组是有符号的,那么这行let nibble = byteArray[i] &gt;&gt;&gt; 4需要改为let nibble = byteArray[i] &gt;&gt;&gt; 4 &amp; 0xF;
      【解决方案4】:

      由于这是“js byte to hex”的第一个 Google 搜索结果,我需要一些时间来了解 Bergi 的功能,所以我重写了该功能并添加了一些 cmets,以便我更容易理解:

      function byteToHex(byte) {
        // convert the possibly signed byte (-128 to 127) to an unsigned byte (0 to 255).
        // if you know, that you only deal with unsigned bytes (Uint8Array), you can omit this line
        const unsignedByte = byte & 0xff;
      
        // If the number can be represented with only 4 bits (0-15), 
        // the hexadecimal representation of this number is only one char (0-9, a-f). 
        if (unsignedByte < 16) {
          return '0' + unsignedByte.toString(16);
        } else {
          return unsignedByte.toString(16);
        }
      }
      
      // bytes is an typed array (Int8Array or Uint8Array)
      function toHexString(bytes) {
        // Since the .map() method is not available for typed arrays, 
        // we will convert the typed array to an array using Array.from().
        return Array.from(bytes)
          .map(byte => byteToHex(byte))
          .join('');
      }
      

      对于只能用 4 位显示的数字,OP 忘记添加前导 0

      【讨论】:

        【解决方案5】:

        使用 Array.reduce() 的更简洁和高性能(参见 https://jsperf.com/byte-array-to-hex-string)替代方案:

        function toHexString(byteArray) {
          return byteArray.reduce((output, elem) => 
            (output + ('0' + elem.toString(16)).slice(-2)),
            '');
        }
        

        (也没有“& 0xFF”,因为在我看来,如果传入的数组包含大于 255 的值,则输出应该是混乱的,这样用户可以更容易地看到他们的输入是错误的。)

        【讨论】:

        • 请注意,如果 byteArray 包含从 -128 到 127 而不是 0 到 255 的有符号字节,则可能需要 &amp; 0xFF
        • 我很惊讶它的性能更高,因为它的运行时间看起来是字节数组长度的二次方。您是否仅在小型阵列上尝试过(jsperf 链接已失效,顺便说一句)?或者 javascript 在 reduce 的每次迭代中重复使用 output 位置真的很聪明吗?
        【解决方案6】:

        您在十六进制转换中缺少填充。你会想要使用

        function toHexString(byteArray) {
          return Array.from(byteArray, function(byte) {
            return ('0' + (byte & 0xFF).toString(16)).slice(-2);
          }).join('')
        }
        

        以便每个字节都转换为两个十六进制数字。您的预期输出为04812d7e3a9829e5d51bdd64ceb35df060699bc1309731bd6e6f1a5443a7f9ce0af4382fcfd6f5f8a08bb2619709c2d49fb771601770f2c267985af2754e1f8cf9

        【讨论】:

        • @DavidCallanan grantpatternson 正在评论旧版本的答案,我使用 byteArray.map 而不是 Array.from
        • @Bergi 好的,感谢您的澄清,感谢您的回答:)
        【解决方案7】:

        如果输入是Uint8Array这样的类型,使用map()将不起作用:map()的结果也是Uint8Array,它不能保存字符串转换的结果。

        function toHexString(byteArray) {
          var s = '0x';
          byteArray.forEach(function(byte) {
            s += ('0' + (byte & 0xFF).toString(16)).slice(-2);
          });
          return s;
        }
        

        【讨论】:

        • 警告:出现“致命错误:无效标记压缩接近堆限制分配失败 - JavaScript 堆内存不足”。
        【解决方案8】:

        您需要用适当数量的前导零填充十六进制转换。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-01-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-14
          • 2017-06-17
          • 2013-05-29
          • 2017-01-16
          相关资源
          最近更新 更多