web-dev-qa-db-ja.com

node.jsのバッファーにバイナリデータを追加する方法

いくつかのバイナリデータを含むバッファがあります。

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

0x03を追加したいと思います。

バイナリデータを追加するにはどうすればよいですか?私はドキュメントで検索していますが、データを追加するには文字列でなければなりません。そうでない場合、エラーが発生します(TypeError:引数は文字列でなければなりません):

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>

しかし、追加ごとに新しいバッファーをインスタンス化する必要があるため、これは少し効率が悪いようです。

バイナリデータを追加するより良い方法を知っていますか?

[〜#〜] edit [〜#〜]

内部バッファーを使用してファイルにバイトを書き込む 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 を使用します。

63
Gabriel Llamas

Node.jsの回答を更新〜> 0.8

ノードは バッファを連結 できるようになりました。

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

Node.jsの古い回答〜0.6

モジュールを使用して.concat関数などを追加します。

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

私はそれが「純粋な」解決策ではないことを知っていますが、私の目的には非常にうまく機能します。

113
Brad

バッファは常に固定サイズであり、動的にサイズを変更する方法は組み込まれていないため、バッファをより大きなバッファにコピーする方法が唯一の方法です。

ただし、より効率的にするために、バッファを元のコンテンツよりも大きくすることができます。そのため、バッファを再割り当てせずにデータを追加できる「空き」スペースが含まれます。そうすれば、新しいバッファーを作成し、各追加操作で内容をコピーする必要がありません。

8
stewe

これは、純粋なアプローチを必要とするソリューションを探している人を助けるためです。この問題は、JS Bufferオブジェクトだけでなく、さまざまな場所で発生する可能性があるため、理解することをお勧めします。問題が存在する理由とその解決方法を理解することにより、この問題は非常に基本的であるため、将来的に他の問題を解決する能力が向上します。

他の言語でこれらの問題に対処しなければならない私たちにとっては、解決策を考案することは非常に自然ですが、複雑さを抽象化し、一般的に効率的な動的バッファを実装する方法を理解できない人もいます。以下のコードは、さらに最適化される可能性があります。

サンプルのサイズを小さく保つために、readメソッドを未実装のままにしました。

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;
};
6
kmcguire