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


84

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

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>

しかし、追加ごとに新しいバッファをインスタンス化する必要があるため、これは少し非効率に思えます。

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

編集

内部バッファを使用してファイルにバイトを書き込む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
上部のミニ回答が実際の回答であり、質問がここに単独である場合は、読む方が明確です。
あんこ

回答:


140

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

ノードは、それ自体でバッファー連結できるようになりました。

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

Node.jsの古い回答〜0.6

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

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

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


concat。機能は、それを計算デ全体の長さ:(私が投稿したまさに行い、その後、コピーしたすべてのバッファのデータがオフセット調整。
ガブリエル・ラマ

それが機能しなければならない方法です。@steweが指摘したように、メモリの割り当て方法により、バッファは固定サイズにインスタンス化されます。
ブラッド

2
しかし、cには、必要に応じてメモリを動的に拡張するrealloc関数があります。node.jsはこれを知っている必要があります。
ガブリエルラマ2012

1
@GabrielLlamas、リポジトリにパッチを送信することをお勧めします。
ブラッド

11
node.jsに動的バッファーがない理由を見つけました:markmail.org/message/vx2h3uslwgludu3y
Gabriel Llamas

10

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

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


8

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

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

例のサイズを小さく保つために、readメソッドは実装されていません。

reallocC関数(又は固有の配分を有する任意の言語の取引)が割り当ては既存のデータを移動アウトとサイズが拡大されることを保証するものではない-時にはそれが可能です。したがって、不明な量のデータを格納する必要があるほとんどのアプリケーションは、再割り当ての頻度が非常に低い場合を除いて、以下のような方法を使用し、常に再割り当てするわけではありません。これは基本的に、ほとんどのファイルシステムがファイルへのデータの書き込みを処理する方法です。ファイルシステムは単に別のノードを割り当て、すべてのノードをリンクしたままにします。そこから読み取ると、複雑さが抽象化され、ファイル/バッファーが単一の連続したバッファーのように見えます。

単に高性能の動的バッファを提供することの難しさを理解したい場合は、以下のコードを表示するだけで、メモリヒープアルゴリズムとプログラムでのメモリヒープの動作について調査する必要があります。

ほとんどの言語は、パフォーマンス上の理由から固定サイズのバッファーを提供し、次にサイズが動的な別のバージョンを提供します。一部の言語システムは、コア機能を最小限に抑え(コア配布)、開発者が追加またはより高いレベルの問題を解決するためのライブラリを作成することを推奨するサードパーティシステムを選択します。これが、言語が一部の機能を提供しない理由を疑問視する理由です。この小さなコア機能により、言語の維持と拡張のコストを削減できますが、最終的には独自の実装を作成するか、サードパーティに依存することになります。

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];、が発生します。これにより、余分なハッシュルックアップ、多数の追加の配列チェック、および1バイトごとに2つのSMI整数チェックが不必要に発生します。パフォーマンスが必要な場合は、この回答を使用しないことを強くお勧めします。代わりに、目的のサイズの新しい配列を割り当て、データを新しい配列にコピーします。それがJavaの機能であり、非常に高速です。
ジャックギフィン

0

特定の場所にバイトを挿入します。

insertToArray(arr,index,item) {
   return Buffer.concat([arr.slice(0,index),Buffer.from(item,"utf-8"),arr.slice(index)]);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.