如何将二进制数据附加到node.js中的缓冲区


81

我有一些二进制数据的缓冲区:

var b = new Buffer ([0x00, 0x01, 0x02]);

我想追加0x03

如何附加更多二进制数据?我正在搜索文档,但是要附加数据,它必须是字符串,否则,将发生错误(TypeError:Argument必须是string):

var b = new Buffer (256);
b.write ("hola");
console.log (b.toString ("utf8", 0, 4)); //hola
b.write (", adios", 4);
console.log (b.toString ("utf8", 0, 11)); //hola, adios

然后,我在这里看到的唯一解决方案是为每个附加的二进制数据创建一个新缓冲区,并将其复制到具有正确偏移量的主缓冲区中:

var b = new Buffer (4); //4 for having a nice printed buffer, but the size will be 16KB
new Buffer ([0x00, 0x01, 0x02]).copy (b);
console.log (b); //<Buffer 00 01 02 00>
new Buffer ([0x03]).copy (b, 3);
console.log (b); //<Buffer 00 01 02 03>

但这似乎效率不高,因为我必须为每个追加实例化一个新的缓冲区。

您知道附加二进制数据的更好方法吗?

编辑

我写了一个BufferedWriter,它使用内部缓冲区将字节写入文件。与BufferedReader相同,但用于写入。

一个简单的例子:

//The BufferedWriter truncates the file because append == false
new BufferedWriter ("file")
    .on ("error", function (error){
        console.log (error);
    })

    //From the beginning of the file:
    .write ([0x00, 0x01, 0x02], 0, 3) //Writes 0x00, 0x01, 0x02
    .write (new Buffer ([0x03, 0x04]), 1, 1) //Writes 0x04
    .write (0x05) //Writes 0x05
    .close (); //Closes the writer. A flush is implicitly done.

//The BufferedWriter appends content to the end of the file because append == true
new BufferedWriter ("file", true)
    .on ("error", function (error){
        console.log (error);
    })

    //From the end of the file:
    .write (0xFF) //Writes 0xFF
    .close (); //Closes the writer. A flush is implicitly done.

//The file contains: 0x00, 0x01, 0x02, 0x04, 0x05, 0xFF

最后更新

使用concat


3
如果顶部的迷你答案是实际答案,而这里的问题是单独的,那么阅读起来会更清楚。
Anko 2014年

Answers:


139

更新了Node.js的答案〜> 0.8

Node现在可以自己连接缓冲区

var newBuffer = Buffer.concat([buffer1, buffer2]);

Node.js的旧答案〜0.6

我使用模块来添加.concat功能,其中包括:

https://github.com/coolaj86/node-bufferjs

我知道这不是一个“纯粹”的解决方案,但是对于我的目的来说,它非常有效。


concat功能不正是我已经张贴:(它计算德总长度,然后将所有的缓冲区调整的数据偏移。
加布里埃尔·拉马斯

这就是它必须起作用的方式。正如@stewe指出的那样,由于分配内存的方式,缓冲区被实例化为固定大小。
Brad 2012年

2
但是在c语言中,我们具有realloc函数,可以在必要时动态扩展内存。node.js应该知道这一点。
加布里埃尔·拉马斯

1
@GabrielLlamas,我建议向其存储库提交补丁。
布拉德

11
我发现了为什么node.js没有动态缓冲区的原因:markmail.org/message/vx2h3uslwgludu3y
Gabriel Llamas

10

缓冲区的大小始终是固定的,没有内置的方法可以动态调整它们的大小,因此将其复制到较大缓冲区的方法是唯一的方法。

但是,为了提高效率,您可以使Buffer大于原始内容,因此它包含一些“可用”空间,您可以在其中添加数据而无需重新分配Buffer。这样,您无需创建新的Buffer并在每个追加操作中复制内容。


8

这是为了帮助任何来这里寻求纯方法的解决方案的人。我建议您理解此问题,因为它可能会在许多不同的地方发生,而不仅仅是JS Buffer对象。通过了解问题存在的原因以及如何解决问题,您将在将来提高解决其他问题的能力,因为这一问题是如此基础。

对于那些不得不用其他语言来解决这些问题的人,设计一个解决方案是很自然的,但是有些人可能没有意识到如何抽象出复杂性并实现通常有效的动态缓冲区。以下代码可能具有进一步优化的潜力。

为了使示例保持较小的大小,我没有执行read方法。

realloc在C函数(或任何语言的交易具有内在分配)并不能保证分配的大小会与出把现有的数据进行扩展-虽然有时它是可能的。因此,大多数应用程序在需要存储未知数量的数据时将使用如下所示的方法,并且不会不断重新分配,除非重新分配很少发生。本质上,这就是大多数文件系统处理将数据写入文件的方式。文件系统只是分配另一个节点,并将所有节点链接在一起,当您从中读取复杂性时,它就被抽象化了,因此文件/缓冲区似乎是单个连续缓冲区。

对于那些希望了解仅提供高性能动态缓冲区的困难的人,您只需查看下面的代码,并对内存堆算法以及程序中的内存堆工作方式进行一些研究。

出于性能原因,大多数语言将提供固定大小的缓冲区,然后提供动态大小的另一个版本。一些语言系统选择将核心功能保持在最低限度(核心分发)的第三方系统,并鼓励开发人员创建库来解决其他或更高级别的问题。这就是为什么您可能会质疑某种语言为什么不提供某些功能的原因。这种小的核心功能可以降低维护和增强语言的成本,但是最终您必须编写自己的实现或依赖第三方。

var Buffer_A1 = function (chunk_size) {
    this.buffer_list = [];
    this.total_size = 0;
    this.cur_size = 0;
    this.cur_buffer = [];
    this.chunk_size = chunk_size || 4096;

    this.buffer_list.push(new Buffer(this.chunk_size));
};

Buffer_A1.prototype.writeByteArrayLimited = function (data, offset, length) {
    var can_write = length > (this.chunk_size - this.cur_size) ? (this.chunk_size - this.cur_size) : length;

    var lastbuf = this.buffer_list.length - 1;

    for (var x = 0; x < can_write; ++x) {
        this.buffer_list[lastbuf][this.cur_size + x] = data[x + offset];
    }

    this.cur_size += can_write;
    this.total_size += can_write;

    if (this.cur_size == this.chunk_size) {
        this.buffer_list.push(new Buffer(this.chunk_size));
        this.cur_size = 0;
    }

    return can_write;
};

/*
    The `data` parameter can be anything that is array like. It just must
    support indexing and a length and produce an acceptable value to be
    used with Buffer.
*/
Buffer_A1.prototype.writeByteArray = function (data, offset, length) {
    offset = offset == undefined ? 0 : offset;
    length = length == undefined ? data.length : length;

    var rem = length;
    while (rem > 0) {
        rem -= this.writeByteArrayLimited(data, length - rem, rem);
    }
};

Buffer_A1.prototype.readByteArray = function (data, offset, length) {
    /*
        If you really wanted to implement some read functionality
        then you would have to deal with unaligned reads which could
        span two buffers.
    */
};

Buffer_A1.prototype.getSingleBuffer = function () {
    var obuf = new Buffer(this.total_size);
    var cur_off = 0;
    var x;

    for (x = 0; x < this.buffer_list.length - 1; ++x) {
        this.buffer_list[x].copy(obuf, cur_off);
        cur_off += this.buffer_list[x].length;
    }

    this.buffer_list[x].copy(obuf, cur_off, 0, this.cur_size);

    return obuf;
};

使用此解决方案时,我建议您格外小心。如果您想要可调整大小的缓冲区的原因是性能,请不要使用this。写入可调整大小的数组的每个字节都会产生this.buffer_list[lastbuf][this.cur_size + x] = data[x + offset];,这不必要地引入了额外的哈希查找,大量的额外数组检查以及每个单个字节两个SMI整数检查。如果性能是您想要的,我强烈建议您不要使用此答案。而是分配一个所需大小的新数组,然后将数据复制到新数组中。这就是Java的功能,而且速度非常快。
杰克·吉芬

0

将字节插入特定位置。

insertToArray(arr,index,item) {
   return Buffer.concat([arr.slice(0,index),Buffer.from(item,"utf-8"),arr.slice(index)]);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.