node.js의 버퍼에 바이너리 데이터를 추가하는 방법


84

바이너리 데이터가있는 버퍼가 있습니다.

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 2014

답변:


140

Node.js ~> 0.8에 대한 업데이트 된 답변

노드는 이제 자체적으로 버퍼연결할 수 있습니다.

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

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

이 솔루션을 사용하는 동안 극도의주의를 권고합니다. 크기를 조정할 수있는 버퍼를 원하는 이유가 성능 인 경우 this를 사용하지 마십시오 . 크기 조정이 가능한 배열에 기록 된 모든 단일 바이트 this.buffer_list[lastbuf][this.cur_size + x] = data[x + offset];는 추가 해시 조회, 많은 추가 배열 검사 및 모든 단일 바이트에 대해 두 번의 SMI 정수 검사를 불필요하게 도입합니다. 성능이 당신이 원하는 것이라면이 대답을 사용하지 말 것을 강력히 촉구합니다. 대신 원하는 크기의 새 배열을 할당하고 데이터를 새 배열에 복사하십시오. 이것이 Java가하는 일이며 정말 빠릅니다.
Jack Giffin

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.