我有一个Javascript ArrayBuffer,我希望被转换成一个十六进制字符串。
有人知道我可以调用的函数还是已经存在的预先编写的函数?
我只能找到数组缓冲区到字符串函数,但我希望数组缓冲区的十六进制代替。
发布于 2016-10-13 22:20:28
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
)放在一起。下面是另一个比较容易理解的更长的实现,但本质上也是这样:
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
发布于 2018-06-08 19:23:16
下面是一个甜蜜的ES6解决方案,它使用padStart
,避免了基于原型调用的可接受答案的解决方案。它实际上也更快。
function bufferToHex (buffer) {
return [...new Uint8Array (buffer)]
.map (b => b.toString (16).padStart (2, "0"))
.join ("");
}
这是如何运作的:
Array
是从保存缓冲区数据的Uint8Array
创建的。这样我们就可以修改数组以保存以后的字符串值。Array
项都映射到它们的十六进制代码,并填充0
字符。发布于 2019-03-16 18:51:20
以下是按速度顺序将ArrayBuffer
编码为十六进制的几种方法。所有的方法最初都是在火狐上测试的,但后来我在Chrome (V8)中进行了测试。在Chrome中,方法基本上是相同的,但它确实有一些细微的差别--重要的是#1是所有环境中速度最快的方法,有很大的差距。
如果要查看当前选择的回答有多慢,可以继续滚动到此列表的底部。
TL;DR
方法1(就在下面)是我测试的最快的编码方法。如果出于某些很好的原因,您需要支持IE,那么在预计算十六进制时,可能需要用方法6中使用的.padStart
调用替换.slice
调用,以确保每个八进制都是2个字符。
1.预先计算的十六进制w/ for
环(最快/基线)
这种方法计算无符号字节:[0, 255]
的每个可能值的两个字符十六进制八进制,然后通过八进制字符串数组映射ArrayBuffer
中的每个值。对于使用此方法的原始回答,应归功于Aaron。
注意: 如Cref所述,您可能会在V8中获得性能提升(Chrome/Chrome/Edge/Brave/等等)。通过使用循环将十六进制连接到一个大字符串中,然后在循环后返回字符串。V8似乎很好地优化了字符串连接,而火狐的性能更好,它构建了一个数组,然后
.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("");
}
2.预先计算的十六进制w/ Array.map
(~30%慢)
与上面的方法一样,我们预先计算了一个数组,其中每个索引的值是索引值的十六进制字符串,但是我们使用了一个hack,其中我们用缓冲区调用Array
prototype的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("");
}
3.预先计算的ASCII字符编码(~230%慢)
这是个令人失望的实验。我写这个函数是因为我认为它会比亚伦预先计算出来的十六进制八进制更快--小子,我错了LOL。当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);
}
4. Array.prototype.map()
w/ padStart()
(~290% )
该方法使用Number.toString()
方法映射一个字节数组,以获得十六进制,然后在必要时通过String.padStart()
方法将八进制填充为"0“。
的重要性:
String.padStart()
是一个相对较新的标准,所以如果您计划支持早于2017年左右的浏览器或Internet,则不应该使用此标准或方法#5。如果你的用户还在使用IE,你应该到他们家安装Chrome/Firefox。帮我们大家个忙。*^D
function hex(arrayBuffer)
{
return Array.prototype.map.call(
new Uint8Array(arrayBuffer),
n => n.toString(16).padStart(2, "0")
).join("");
}
5. Array.from().map()
w/ padStart()
(~370% )
这与#4相同,但是我们没有使用Array
原型,而是从Uint8Array
中创建一个实际的数字数组,并直接调用map()
。不过,我们是按速度付钱的。
function hex(arrayBuffer)
{
return Array.from(new Uint8Array(arrayBuffer))
.map(n => n.toString(16).padStart(2, "0"))
.join("");
}
6. 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("");
}
第1课
预计算有时会是一种非常有效的内存-速度权衡。理论上说,预先计算的十六进制数组仅能存储1024字节(256个可能的十六进制值为⨉2字符/值⨉2字节/字符,用于大多数/所有浏览器使用的UTF-16字符串表示),这在现代计算机中是没有的。实际上,这里有更多的字节用于存储数组和字符串长度,并且可能是类型信息,因为这是JavaScript,但是内存的使用仍然可以忽略不计,以提高性能。
第二课
帮助优化编译器。浏览器的JavaScript编译器经常尝试理解您的代码,并将其分解为尽可能快的机器代码供您的JavaScript执行。因为JavaScript是一种非常动态的语言,这可能很难做到,有时浏览器会放弃,留下各种各样的类型检查,更糟糕的是,它无法确定x是否真的是字符串或数字,反之亦然。使用现代函数式编程,例如内置.map
类的Array
方法,可能会给浏览器带来麻烦,因为回调函数可以捕获外部变量,并做各种经常会影响性能的事情。for -循环是经过充分研究和相对简单的构造,因此浏览器开发人员结合了编译器优化您的JavaScript for-循环的各种技巧。保持简单。
https://stackoverflow.com/questions/40031688
复制相似问题