javascript 中的字节数组到十六进制字符串的转换

Byte array to Hex string conversion in javascript

我有一个 [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

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

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

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

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

如果输入的类型类似于 Uint8Array,则使用 map() 将不起作用:map() 的结果也是 Uint8Array,它不能容纳字符串转换结果。

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

使用 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 的值,则输出应该是混乱的,这样用户可以更容易地看到他们的输入是错误的。 )

由于这是 "js byte to hex" 的第一个 Google 命中,我需要一些时间来理解 Bergi 的功能,因此我重写了该函数并添加了一些注释,以便于我更容易理解:

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('');
}
  • 有关 const unsignedByte = byte & 0xff 部分的更多信息,请查看 What does AND 0xFF do?
  • Array.from is not available in every browser (e.g. not in IE11), check How to convert a JavaScript Typed Array into a JavaScript Array了解更多信息

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

所有以前的解决方案都有效,但它们都需要创建许多字符串并对创建的字符串进行连接和切片。我在想,既然有了类型化数组,就必须有更好的方法来解决这个问题。我最初使用 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);
}

将字节数组转换为十六进制数组时,我们必须考虑如何将它们作为有符号数。如果是这样,我们必须先将它们转换为十进制数。 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'); 
});

这是 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('');
    };