如何在Javascript中存储字节数组

88

我将在Javascript中存储一个大型字节数组(很可能超过一百万个)。如果我使用普通数组和普通数字,则需要8 MB,因为数字以IEEE doubles的形式存储,但是如果我可以将其存储为字节,则仅需要1 MB。

出于明显的原因,我希望避免浪费那么多空间。是否有一种可以将字节存储为字节而不是doubles的方法?对于我来说,浏览器兼容性不是问题,只要它可以在Chrome中工作即可。如果这是在HTML5中,是否有区别呢?


12
类型化数组。 - Pointy
这些链接可能会对您有所帮助:http://stackoverflow.com/questions/5612637/storing-byte-array-in-html5-sqlite-database 和 http://siphon9.net/loune/2011/05/javascript-arraybuffer-binary-handling-in-javascript/ - Gyan Chandra Srivastava
@Pointy:字节的类型化数组?http://www.khronos.org/registry/typedarray/specs/latest/#7 你能详细解释一下吗?我想要理解它。 - naveen
@naveen 对于字节(8位)的数组类型,如果你想要字节有符号,那么类型为 Int8Array;如果你不需要字节有符号,那么类型为 Uint8Array - HoLyVieR
7
关于“只要在Chrome上能用”这句话,我很想给你打个负分(虽然我没这么做,但我很想),因为这种想法导致我们留下了一批只能在IE6上运行的网站。如果你所需的功能只有在Chrome上才能实现,那就只能如此了,但不要限制你的网站。有很多浏览器可供选择,它们都支持HTML5。尽可能地面向广泛的受众编写你的网站。 - Spudley
20
@Spudley 这个网站不是为了公众开放,只是为了我一个人而写的。请理解。 - Kendall Frey
4个回答

91

通过使用类型化数组,您可以存储以下这些类型的数组:

类型 取值范围 大小(字节)
Int8Array -128 到 127 1
Uint8Array 0 到 255 1
Uint8ClampedArray 0 到 255 1
Int16Array -32768 到 32767 2
Uint16Array 0 到 65535 2
Int32Array -2147483648 到 2147483647 4
Uint32Array 0 到 4294967295 4
Float32Array -3.4E38 到 3.4E38 4
Float64Array -1.8E308 到 1.8E308 8
BigInt64Array -2^63 到 2^63 - 1 8
BigUint64Array 0 到 2^64 - 1 8

在 Stack Snippets 和 JSFiddle 中的演示

var array = new Uint8Array(100);
array[42] = 10;
console.log(array[42]);


11
是的!使用Float32Array将数组长度为800,000+元素的生成时间从2.4秒缩短至0.12秒,这真是神奇的差异! - Robbie Wxyz
1
我在将由fetch() promise返回的Blob转换为C# byte[]时遇到了问题。您有什么建议或智慧可以提供吗? - Reahreic

19
var array = new Uint8Array(100);    
array[10] = 256;
array[10] === 0 // true

我在Firefox和Chrome中验证过,确实是一组字节:

var array = new Uint8Array(1024*1024*50);  // allocates 50MBytes

256 === 0 是怎么回事?难道不应该是 256 吗? - 1.21 gigawatts
32
数组是一个 uint8 类型的数组,256 超出了一个字节的范围,并使该值等于 0。 - gsempe

2
你可以将数据存储在一个固定大小的字符串数组中。访问该字符串数组中的任何特定字符应高效,并将该字符视为字节。
可能会有兴趣看到你想要支持的操作,可以表达为接口,以使问题更具体化。

1
我希望你能为我提供一个更精确、有用的答案。以下是真正的答案(如果你需要一个字节数组,请相应地进行调整;显然,数学计算将会失准一个8位:1字节的因素):
class BitArray {
  constructor(bits = 0) {
    this.uints = new Uint32Array(~~(bits / 32));
  }

  getBit(bit) {
    return (this.uints[~~(bit / 32)] & (1 << (bit % 32))) != 0 ? 1 : 0;
  }

  assignBit(bit, value) {
    if (value) {
      this.uints[~~(bit / 32)] |= (1 << (bit % 32));
    } else {
      this.uints[~~(bit / 32)] &= ~(1 << (bit % 32));
    }
  }

  get size() {
    return this.uints.length * 32;
  }

  static bitsToUints(bits) {
    return ~~(bits / 32);
  }
}

使用方法:

let bits = new BitArray(500);
for (let uint = 0; uint < bits.uints.length; ++uint) {
  bits.uints[uint] = 457345834;
}
for (let bit = 0; bit < 50; ++bit) {
  bits.assignBit(bit, 1);
}
str = '';
for (let bit = bits.size - 1; bit >= 0; --bit) {
  str += bits.getBit(bit);
}
str;

输出:

"00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000101000101100101010
 00011011010000111111111111111111
 11111111111111111111111111111111"
注意:如果这个类作为全局变量创建,例如在Linux上的Firefox 76.0控制台中分配位(即每1000万次分配需要大约2秒),那么速度会非常慢...但是,如果将其作为变量创建(即let bits = new BitArray(1e7);),那么它就会非常快(即每1000万次分配大约300毫秒)!

更多信息,请参见此处:

请注意,我使用Uint32Array是因为没有直接拥有可直接交互的位/字节数组(且仅支持32位)的方法,同时即使有BigUint64Array,JS也仅支持32位。

位运算符将它们的操作数视为32位序列。...所有位运算符的操作数都被转换为32位整数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接