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 |
此功能分为四个步骤:
下面是另一个更长的实现,虽然有点容易理解,但本质上是相同的:
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解决方案,它使用
1 2 3 4 5 6 | function bufferToHex (buffer) { return Array .from (new Uint8Array (buffer)) .map (b => b.toString (16).padStart (2,"0")) .join (""); } |
工作原理:
这是另一个解决方案,在Chrome(可能还有节点)上,其速度比使用
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
以下是按速度顺序将
如果要查看当前选定答案的速度,可以继续并滚动到此列表的底部。
注意复制粘贴
做个好男孩/女孩,并使用解决方案1。它是最快且受支持最好的。在浏览器中唯一快速编码为十六进制的方法是编写优化的C代码并编译为Web Assembly。
1.预计算的带有
此方法为无符号字节的每个可能值(
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.带有
与上述方法相同,在该方法中,我们预先计算了一个数组,其中每个索引的值都是该索引值的十六进制字符串,但是我们使用了hack方法,在该方法中,我们使用缓冲区调用了
IMPORTANT: You cannot use
new Uint8Array(arrayBuffer).map(...) . AlthoughUint8Array implements theArrayLike interface, itsmap method will return anotherUint8Array which cannot contain strings (hex octets in our case), hence theArray 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位的十六进制字符,并使用
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.
此方法使用
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.
这与#4相同,但不是
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.
这是选定的答案,除非您是典型的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循环,现在它更快了。
我用它来六角转储
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> |