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

javascript中字节数组到十六进制字符串的转换,javascript,arrays,bitcoin,data-conversion,Javascript,Arrays,Bitcoin,Data Conversion,我有一个[4,-101122,-41,-30,23,-28,3,…]形式的字节数组,我想将其转换为6d69f597b217fa333246c2c8 我正在使用下面的函数 function toHexString(bytes) { return bytes.map(function(byte) { return (byte & 0xFF).toString(16) }).join('') } 这给了我一个相同形式的字符串,但我怀疑这不是一个有效的转换,因为十六进制字符串比

我有一个
[4,-101122,-41,-30,23,-28,3,…]
形式的字节数组,我想将其转换为
6d69f597b217fa333246c2c8
我正在使用下面的函数

function toHexString(bytes) {
  return bytes.map(function(byte) {
    return (byte & 0xFF).toString(16)
  }).join('')
}
这给了我一个相同形式的字符串,但我怀疑这不是一个有效的转换,因为十六进制字符串比预期的要短一点。我认为翻译应该得到“0a10a6dc”。 请告诉我是否我错了,或者这是正确的转换,但可能我没有使用正确的字节数组

字节数组
4,-127,45126,58,-104,41,-27,-43,27,-35100,-50,-77,93,-16,96105,-101,-63,48,-105,49,-67110111,26,84,67,-89,-7,-50,10,-12,56,47,-49,-42,-11,-8,-96,-117,-78,97,-105,9,-62,-44,-97,-73113,96,23112,-14,-62103,-104,90,-14117,78,31,-116,-7


相应的转换
4812D7E3A9829E5D51BDD64CEB35DF060699BC1309731BD6E6F1A5443A7F9CEAF4382FCD6F5F8A08BB261979C2D49FB771601770F2C267985AF2754E1F8CF9
十六进制转换中缺少填充。你会想用

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

这样每个字节就可以转换成两个十六进制数字。您的预期输出将是
04812d7e3a9829e5d51bdd64ceb35df060699bc1309731bd6e6f1a5443a7f9ce0af4382fcfd6f5f8a08bb2619709c2d49fb771601770f2c267985af2754e1f8cf9
您需要用适当数量的前导零填充十六进制转换。

使用
map()如果输入的类型类似于
Uint8Array
map()
的结果也是
Uint8Array
,无法保存字符串转换的结果

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

使用Array.reduce()的更简洁、更高效的替代方法(请参阅):


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

因为这是谷歌第一次点击“js byte to hex”,我需要一些时间来理解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('');
}
函数byteToHex(字节){
//将可能有符号的字节(-128到127)转换为无符号字节(0到255)。
//如果您知道只处理无符号字节(Uint8Array),那么可以省略这一行
常量unsignedByte=byte&0xff;
//如果数字只能用4位(0-15)表示,
//这个数字的十六进制表示形式只有一个字符(0-9,a-f)。
if(无符号字节<16){
返回“0”+无符号字节.toString(16);
}否则{
返回unsignedByte.toString(16);
}
}
//字节是类型化数组(Int8Array或Uint8Array)
函数toHexString(字节){
//由于.map()方法不适用于类型化数组,
//我们将使用array.from()将类型化数组转换为数组。
返回数组.from(字节)
.map(字节=>byteToHex(字节))
.加入(“”);
}
  • 有关
    const unsignedByte=byte&0xff部分的更多信息,请检查
  • 并非在每个浏览器中都可用(例如,IE11中不可用),请查看以了解更多信息

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

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

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

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);
}
函数到十六进制字符串(byteArray){
//const chars=新缓冲区(byteArray.length*2);
const chars=新的uint8数组(byteArray.length*2);
常数alpha='a'.charCodeAt(0)-10;
常量数字='0'。字符编码(0);
设p=0;
for(设i=0;i>>4;
字符[p++]=半字节>9?半字节+字母:半字节+数字;
nibble=byteArray[i]&0xF;
字符[p++]=半字节>9?半字节+字母:半字节+数字;
}
//返回chars.toString('utf8');
返回字符串.fromCharCode.apply(null,chars);
}

<代码> > p>当将字节数组转换为十六进制数组时,我们必须考虑它们如何可以是带符号的数字。如果是这样,我们必须先把它们转换成十进制数。然后,我们可以使用
.toString(16)
方法将其转换为十六进制

const hexArr=byteArr.map((字节)=>{
如果(字节<0){
字节=-((字节^0xff)+1);//将2s补码转换为十进制数
}
//在开头添加填充,确保其长度始终为2个字符,否则“01”将为“1”
返回字节.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('');
    };
函数buf2hex(缓冲区){
var u=新的UINT8阵列(缓冲区),
a=新阵列(u.长度),
i=u.长度;
while(i--)//映射到十六进制
a[i]=(u[i]<16?'0':'')+u[i].toString(16);
u=null;//释放内存
返回a.join(“”);
};

对不起,我已经更新了代码。在发布之前,我更改了变量,但现在我使用的是原始代码。如果byteArray是一个不能保存字符串值的类型化数组,这将不起作用:如果传入一个Uint8Array,map的结果也将是一个Uint8Array,因此像“ff”这样的值将无法加入()。几乎所有现代浏览器都支持类型化数组(甚至IE 10和11)。如果输入数组是有符号的,则需要将此行
let nibble=byteArray[i]>>4
更改为
let nibble=byteArray[i]>>4&0xF
注意,如果byteArra
    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('');
    };