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

103

我有一个包含二进制数据的缓冲区:

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

我想要添加 0x03

如何添加更多的二进制数据? 我在文档中搜索,但是对于添加数据而言,它必须是一个字符串,否则会出现错误(TypeError: Argument must be a 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
4个回答

176

为Node.js ~>0.8更新的答案

现在,Node能够自行合并缓冲区

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

Node.js ~0.6的旧答案

我使用一个模块来添加.concat函数和其他一些函数:

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

我知道这不是一个“纯净”的解决方案,但它非常适合我的需求。


concat 函数正是我所发布的内容 :(。它计算总长度,然后复制所有缓冲区的数据并调整偏移量。 - Gabriel Llamas
这就是它应该工作的方式。正如@stewe所指出的那样,由于内存分配的方式,缓冲区被实例化为固定大小。 - Brad
2
但是在C语言中,我们有realloc函数可以在需要时动态扩展内存。Node.js应该知道这一点。 - Gabriel Llamas
1
@GabrielLlamas,我建议您向他们的代码库提交一个补丁。 - Brad
16
我发现了为什么Node.js没有动态缓冲区:http://markmail.org/message/vx2h3uslwgludu3y。 - Gabriel Llamas
1
@GabrielLlamas realloc 有很大的概率会复制所有数据。 - Qix - MONICA WAS MISTREATED

12

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

然而,为了更有效率,您可以使缓冲区比原始内容更大,这样它就包含一些“自由”空间,您可以在不重新分配缓冲区的情况下添加数据。这样,您就不需要在每个附加操作中创建新的缓冲区并复制内容。


9
这是为了帮助任何需要纯粹方法解决方案的人。我建议理解这个问题,因为它可能发生在许多不同的地方,而不仅仅是JS缓冲对象。通过理解问题存在的原因以及如何解决它,您将提高自己未来解决其他问题的能力,因为这个问题非常基础。
对于我们中的一些人来说,在其他语言中处理这些问题是很自然的,但有些人可能不知道如何抽象出复杂性并实现通用有效的动态缓冲区。下面的代码可能有进一步优化的潜力。
我留下了未实现的读取方法,以使示例尺寸更小。
在C(或任何处理内部分配的语言)中,realloc函数不能保证扩展分配的大小而不移动现有数据,尽管有时可能是可能的。因此,大多数应用程序在需要存储未知数量的数据时会使用以下方法,并且不会经常重新分配,除非重新分配非常不频繁。这基本上是大多数文件系统处理向文件写入数据的方式。文件系统只需分配另一个节点并将所有节点链接在一起,当您从中读取时,复杂性被抽象化,使得文件/缓冲区看起来像是单个连续缓冲区。
对于那些希望了解提供高性能动态缓冲区的困难的人,您只需要查看下面的代码,并进行一些关于内存堆算法以及程序的内存堆如何工作的研究。
大多数语言将为了性能原因提供固定大小的缓冲区,然后提供另一个动态大小的版本。一些语言系统选择第三方系统,其中他们保持核心功能最小(核心分发),并鼓励开发人员创建库来解决其他或更高级别的问题。这就是为什么您可能会质疑某种语言为什么不提供某些功能。这种小型核心功能可以降低维护和增强语言的成本,但最终您将不得不编写自己的实现或依赖第三方。
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;
};

1
我建议在使用此解决方案时要极度谨慎。如果你想要可调整大小的缓冲区是为了提高性能,请不要使用此方法。每写入一个字节到可调整大小的数组中都会产生this.buffer_list[lastbuf][this.cur_size + x] = data[x + offset];,这无意中引入了额外的哈希查找、大量的额外数组检查和两个SMI整数检查。如果你想要性能,我强烈建议你不要使用这个答案。相反,分配一个所需大小的新数组并将数据复制到新数组中。这就是Java所做的,而且非常快。 - Jack G

-1

将字节插入特定位置。

insertToArray(arr,index,item) {
   return Buffer.concat([arr.slice(0,index),Buffer.from(item,"utf-8"),arr.slice(index)]);
}

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