我有一个 Javascript ArrayBuffer,我想将其转换为十六进制字符串。
任何人都知道我可以调用的函数或已经存在的预先编写的函数?
我只能找到数组缓冲区到字符串函数,但我想要数组缓冲区的十六进制转储。
我有一个 Javascript ArrayBuffer,我想将其转换为十六进制字符串。
任何人都知道我可以调用的函数或已经存在的预先编写的函数?
我只能找到数组缓冲区到字符串函数,但我想要数组缓冲区的十六进制转储。
function buf2hex(buffer) { // buffer is an ArrayBuffer
return [...new Uint8Array(buffer)]
.map(x => x.toString(16).padStart(2, '0'))
.join('');
}
// EXAMPLE:
const buffer = new Uint8Array([ 4, 8, 12, 16 ]).buffer;
console.log(buf2hex(buffer)); // = 04080c10
此功能分四步工作:
x
数组,它将该元素转换为十六进制字符串(例如,12
变成c
)。c
变成0c
)。下面是另一个更容易理解的更长的实现,但本质上做同样的事情:
这是一个甜蜜的 ES6 解决方案,使用padStart
并避免了相当混乱的基于原型调用的已接受答案的解决方案。它实际上也更快。
function bufferToHex (buffer) {
return [...new Uint8Array (buffer)]
.map (b => b.toString (16).padStart (2, "0"))
.join ("");
}
这是如何工作的:
Array
是从Uint8Array
保存缓冲区数据的a 创建的。这样我们就可以稍后修改数组以保存字符串值。Array
项目都映射到它们的十六进制代码并用0
字符填充。以下是ArrayBuffer
按速度顺序将 an 编码为十六进制的几种方法。所有方法最初都在 Firefox 中进行了测试,但后来我在 Chrome (V8) 中进行了测试。在Chrome中的方法大多以相同的顺序,但它确实有轻微的differenences -重要的是,#1是由在所有环境中的最快方法巨额保证金。
如果您想查看当前选择的答案有多慢,您可以继续并滚动到此列表的底部。
方法#1(就在这个下面)是我测试过的最快的编码为十六进制字符串的方法。如果出于某些非常好的原因,您需要支持 IE,则在预计算十六进制八位字节以确保每个八位字节为 2 个字符时,您可能需要将.padStart
调用替换为.slice
方法 #6 中使用的技巧。
for
循环的预计算十六进制八位字节(最快/基线)此方法为无符号字节的每个可能值计算 2 个字符的十六进制八位字节:[0, 255]
,然后仅将 中的每个值映射ArrayBuffer
到八位字节字符串数组。使用此方法的原始答案归功于 Aaron Watters 。
注意: 正如 Cref 所提到的,通过使用循环将十六进制八位字节连接成一个大字符串,然后在之后返回字符串,您可能会在 V8(Chromium/Chrome/Edge/Brave/等)中获得性能提升环形。V8 似乎很好地优化了字符串连接,而 Firefox 在构建数组然后
.join
在最后将其转换为字符串时表现更好,就像我在下面的代码中所做的那样。这可能是一个微优化,但会随着优化 JS 编译器的奇思妙想而改变。
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("");
}
Array.map
(慢约 30%)与上面的方法相同,我们预先计算了一个数组,其中每个索引的值都是索引值的十六进制字符串,但是我们使用了一种技巧,我们用缓冲区调用Array
原型的map()
方法。这是一种更实用的方法,但如果你真的想要速度,你将始终使用for
循环而不是 ES6 数组方法,因为所有现代 JS 引擎都可以更好地优化它们。
重要提示:您不能使用
new Uint8Array(arrayBuffer).map(...)
. 虽然Uint8Array
实现了ArrayLike
接口,但它的map
方法将返回另一个Uint8Array
不能包含字符串(在我们的例子中为十六进制八位字节)的方法,因此Array
原型黑客。
function hex(arrayBuffer)
{
return Array.prototype.map.call(
new Uint8Array(arrayBuffer),
n => byteToHex[n]
).join("");
}
好吧,这是一个令人失望的实验。我写了这个函数,因为我认为它会比 Aaron 预先计算的十六进制八位字节更快——我错了,哈哈。虽然 Aaron 将整个字节映射到它们相应的 2 字符十六进制代码,但该解决方案使用位移来获取每个字节中前 4 位的十六进制字符,然后是最后 4 位的十六进制字符,并使用String.fromCharCode()
. 老实说,我认为String.fromCharCode()
一定是优化不当,因为它没有被很多人使用,并且在浏览器供应商的优先级列表中排名较低。
const asciiCodes = new Uint8Array(
Array.prototype.map.call(
"0123456789abcdef",
char => char.charCodeAt()
)
);
function hex(arrayBuffer)
{
const buff = new Uint8Array(arrayBuffer);
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);
}
Array.prototype.map()
w/ padStart()
(慢~290%)此方法使用该方法映射字节数组Number.toString()
以获取十六进制,然后在必要时通过该String.padStart()
方法用“0”填充八位字节。
重要提示:
String.padStart()
是一个相对较新的标准,因此如果您计划支持早于 2017 年左右的浏览器或 Internet Explorer,则不应使用此方法或方法 #5。TBH 如果您的用户仍在使用 IE,那么此时您可能应该去他们家安装 Chrome/Firefox。帮我们一个忙。:^D
function hex(arrayBuffer)
{
return Array.prototype.map.call(
new Uint8Array(arrayBuffer),
n => n.toString(16).padStart(2, "0")
).join("");
}
Array.from().map()
w/ padStart()
(慢~370%)这与 #4 相同,但我们不是Array
原型 hack,而是从 中创建一个实际的数字数组Uint8Array
并map()
直接调用它。不过,我们按速度付款。
function hex(arrayBuffer)
{
return Array.from(new Uint8Array(arrayBuffer))
.map(n => n.toString(16).padStart(2, "0"))
.join("");
}
Array.prototype.map()
w/ slice()
(慢~450%)这是选定的答案,除非您是典型的 Web 开发人员并且性能让您感到不安,否则不要使用它(答案 #1 也被许多浏览器支持)。
function hex(arrayBuffer)
{
return Array.prototype.map.call(
new Uint8Array(arrayBuffer),
n => ("0" + n.toString(16)).slice(-2)
).join("");
}
有时,预先计算的东西可能是一种非常有效的内存与速度的权衡。从理论上说,预先计算的十六进制的八位字节的阵列可以被存储在刚刚1024字节(256个可能的十六进制值⨉2个字符/值⨉2个字节/字用于通过多数/所有浏览器所使用的UTF-16字符串表示),这是没有在现代计算机。实际上有更多的字节用于存储数组和字符串长度以及类型信息,因为这是 JavaScript,但内存使用量仍然可以忽略不计,以实现大规模的性能改进。
帮助优化编译器。浏览器的 JavaScript 编译器会定期尝试理解您的代码并将其分解为尽可能快的机器代码,以便您的 CPU 执行。因为 JavaScript 是一种非常动态的语言,所以这可能很难做到,有时浏览器会放弃并留下各种类型的检查,更糟糕的是在幕后,因为它不能确定x确实是一个字符串或数,反之亦然。使用现代函数式编程添加,如.map
内置的方法Array
class 会给浏览器带来麻烦,因为回调函数可以捕获外部变量并执行各种其他经常损害性能的事情。For 循环是经过充分研究和相对简单的构造,因此浏览器开发人员已经为编译器整合了各种技巧来优化您的 JavaScript for 循环。把事情简单化。
这是另一个解决方案,它在 Chrome(也可能是节点)上比使用map
and的其他建议快 3 倍toString
:
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 转换为十六进制的最简单方法:
const buffer = new Uint8Array([ 4, 8, 12, 16 ]);
console.log(Buffer.from(buffer).toString("hex")); // = 04080c10