ホームページ >ウェブフロントエンド >jsチュートリアル >この記事では、Node の Buffer クラスについて詳しく説明します。

この記事では、Node の Buffer クラスについて詳しく説明します。

青灯夜游
青灯夜游転載
2022-12-12 19:36:131894ブラウズ

この記事では、Node の Buffer クラスについて詳しく説明します。皆様のお役に立てれば幸いです。

この記事では、Node の Buffer クラスについて詳しく説明します。

TypedArray が登場する前は、JavaScript 言語は raw binary data(raw binary data) をうまく扱うことができませんでした。 JavaScript は当初、主にブラウザーのスクリプト言語として使用されていたため、ネイティブ バイナリ データを処理する必要があるシナリオはほとんどなかったためです。 Node が登場した後、サーバー側アプリケーションは ファイルの読み書き TCP 接続 などの多数のバイナリ ストリームを処理する必要があるためです。 , ノードは JavaScript (V8) 内にあります。また、新しいデータ型 Buffer が定義されています。 Buffer は Node アプリケーションで広く使用されているため、その使用法を真にマスターすることによってのみ、より優れた Node アプリケーションを作成できます。 [関連するチュートリアルの推奨事項: nodejs ビデオ チュートリアル プログラミング指導 ]

バイナリの基礎


正式なはじめに Buffer の具体的な使い方の前に、バイナリに関する知識を簡単に復習しましょう。

プログラマとして、私たちは皆バイナリについてよく知っている必要があります。コンピュータの基礎となるデータはすべてバイナリ (バイナリ) 形式で保存されているためです。つまり、コンピュータ内のファイルは、プレーン テキスト、画像、ビデオのいずれであっても、コンピュータのハード ドライブ上の 2 つの番号 01 で構成されています。コンピューター サイエンスでは、単一の数値 0 または 1 ビット (ビット) と呼び、8 ビット で ## を形成できます。 #バイト(バイト)。 10 進数 16 が 1 バイトで表される場合、基礎となるストレージ構造は次のとおりです: 16 が 2 進数で表される場合、10 進数よりも 6 桁多いことがわかります。は 2 進数になるため、読み取りと書き込み 截屏2022-10-15 下午2.23.13.png には非常に不便になります。このため、プログラマーは通常、バイナリを直接使用する代わりに、hexadecimal (16 進数) を使用してデータを表すことを好みます。たとえば、CSS を記述するときは、color の値を使用します。16 進数 (例: #FFFFFF) 0 と 1 の束の代わりに。

文字エンコーディングすべてのデータの最下層はバイナリであり、ネットワーク上で送信されるデータもバイナリであるため、この記事はなぜ私たちは今読んでいます

#0

1 の束の代わりに ##中文 はどうでしょうか?ここでは、文字エンコーディングの概念を紹介します。いわゆる 文字エンコードは、単なる マッピング関係テーブル であり、文字 (漢字、英語文字、またはその他の文字) と 2 進数 (数バイトを含む)相互に対応します。たとえば、使い慣れた ascii を使用してエンコードすると、英語文字 a のバイナリ表現は 0b01100001 になります (0b はバイナリです)番号プレフィックス)。したがって、コンピューターが ascii エンコードされた ファイルからバイナリ データ 0b01100001 の文字列を読み取ると、文字 a が画面に表示されます。 a は、コンピューターに保存またはネットワーク上で送信される場合の 0b01100001 のバイナリ データです。 ascii コードに加えて、一般的な文字エンコーディングには utf-8utf-16 などが含まれます。 バッファ

基本的な

バイナリ知識

文字エンコーディングの概念を習得したら、いよいよ正式に学習できるようになります。 ###バッファ###。 Buffer:Node.js の Buffer
クラスは、生のバイナリ データを処理するように設計されています。各バッファは、V8 の外部に割り当てられた生のメモリに対応します。バッファは整数の配列のように動作しますが、サイズ変更はできず、バイナリ データに特化したメソッドが多数あります。バッファ内の整数はそれぞれ 1 バイトを表すため、次の値に制限されます。値は 0 ~ 255 です。

console.log()
を使用して

Buffer インスタンスを出力すると、一連の値が 16 進数値で取得されます。

簡単に言えば、いわゆる バッファ は、V8 ヒープ メモリ の外側にノードによって割り当てられる 固定サイズ メモリ空間です。 console.log を使用して Buffer を出力すると、16 進数 ## の文字列が bytes 単位で出力されます。 # は値を表します。

バッファの作成

Buffer の基本概念を理解した後、Buffer を作成しましょう物体。 Buffer を作成する方法は数多くありますが、一般的な方法は Buffer.allocBuffer.allocUnsafe、および Buffer.from です。

Buffer.alloc(size[, fill[,coding]])

これはバッファを作成する最も一般的な方法です。バッファのサイズを渡すだけで済みます

const buff = Buffer.alloc(5)

console.log(buff)
// Prints: <Buffer 00 00 00 00 00>

上記のコードでは、##5 バイト

のサイズのバッファー領域を作成しました。console.log 関数は、内容を示す 5 つの連続する 16 進数を出力します。現在バッファに保存されています。現在のバッファが 0 で満たされていることがわかります。これはノードのデフォルトの動作です。次の 2 つのパラメータ fillencoding を設定して、Fill を指定できます。初期化中の追加コンテンツ内。 ここで言及しておく価値があるのは、上記のコードでは、

node:buffer

パッケージから明示的にインポートせずに、Node グローバル Buffer オブジェクトを使用しているということです。実際の開発では、 後者の を使用する必要があります:

import { Buffer } from &#39;node:buffer&#39;

Buffer.allocUnsafe(size)

Buffer.allocUnsafe

Buffer.alloc の違いは、allocUnsafe 関数の使用に適用されるメモリ空間が 初期化されていないことです。つまり、前回使用したデータがまだ残っている可能性があるため、データ セキュリティの問題が発生します。 allocUnsafe この関数は、バッファ領域のサイズとして size パラメータを受け取ります。

const buff = Buffer.allocUnsafe(5)

console.log(buff)
// Prints (实际内容可能有出入): <Buffer 8b 3f 01 00 00>
上記の出力結果から判断すると、 の使用を制御できません。 Buffer.allocUnsafe

割り当てられたバッファの内容。割り当てられたメモリが初期化されていないため、この関数は

Buffer.alloc よりも高速にバッファを割り当てます。実際の開発では、実際のニーズに基づいて選択する必要があります。 Buffer.from

この関数は、バッファを作成するために 最も一般的に使用される

関数であり、さまざまな

オーバーロードがありますつまり、渡されるパラメーターが異なれば、動作も異なります。いくつかの一般的なオーバーロードを見てみましょう: Buffer.from(string[, encoding])

渡す最初のパラメータが String## の場合# type の場合、Buffer.from は、文字列のエンコーディング (

encoding

パラメーター、デフォルトは utf8) に基づいて、文字列に対応するバイナリ表現を生成します。例を見てみましょう:

const buff = Buffer.from(&#39;你好世界&#39;)

console.log(buff)
// Prints: <Buffer e4 bd a0 e5 a5 bd e4 b8 96 e7 95 8c>
console.log(buff.toString())
// Prints: &#39;你好世界&#39;
console.log(buff.toString(&#39;ascii&#39;))
// Prints: &#39;&#39;d= e%=d8\x16g\x15\f&#39;&#39;
上記の例では、文字列 "Hello World" を使用してバッファの初期化を完了しました。2 番目の encoding パラメータを渡さなかったため、デフォルトのエンコードは

utf8

です。その後、最初の console.log の出力を見ると、渡した文字列には 4 文字しかありませんが、初期化されたバッファには 12 バイトがあることがわかります。 utf8 エンコーディングの中国語文字は表現するために 3 バイトを使用するためです。次に、buff.toString() メソッドを使用してバフの内容を表示します。toString メソッドのデフォルトのエンコード出力形式は utf8 であるため、2 番目の ## が表示されます。 #console .log は、buff ストレージの内容を正しく出力できます。ただし、3 番目の console.log では、文字エンコーディング タイプが ascii であると指定されており、この時点で大量の文字化けが確認されます。これを見ると、先ほど述べた 文字エンコーディング についての理解がさらに深まったと思います。 Buffer.from(buffer)

Buffer.from で受け取ったパラメータがバッファ オブジェクトの場合、Node は新しい Buffer インスタンスを作成し、それをバッファの内容を新しい Buffer オブジェクトに

copy します。
const buf1 = Buffer.from(&#39;buffer&#39;)
const buf2 = Buffer.from(buf1)

console.log(buf1)
// Prints: <Buffer 62 75 66 66 65 72>
console.log(buf2)
// Prints: <Buffer 62 75 66 66 65 72>

buf1[0] = 0x61

console.log(buf1.toString())
// Prints: auffer
console.log(buf2.toString())
// Prints: buffer

上記の例では、最初にバッファ オブジェクト

buf1 を作成しました。そこに格納されるコンテンツは文字列「buffer」であり、次にこのバッファ オブジェクトを通じて新しいバッファ オブジェクトを初期化しました。 buf2

。このとき、

buf1 の最初のバイトを 0x61 (a のエンコーディング) に変更すると、buf1 の出力が auffer になることがわかりました。 buf2 の内容は変更されていません。これは、Buffer.from(buffer) がデータのコピーであるという見解を裏付けています。

?注意:当Buffer的数据很大的时候,Buffer.from拷贝数据的性能是很差的,会造成CPU占用飙升,主线程卡死的情况,所以在使用这个函数的时候一定要清楚地知道Buffer.from(buffer)背后都做了什么。笔者就在实际项目开发中踩过这个坑,导致线上服务响应缓慢!

Buffer.from(arrayBuffer[, byteOffset[, length]])

说完了buffer参数,我们再来说一下arrayBuffer参数,它的表现和buffer是有很大的区别的。ArrayBuffer是ECMAScript定义的一种数据类型,它简单来说就是一片你不可以直接(或者不方便)使用的内存,你必须通过一些诸如Uint16ArrayTypedArray对象作为View来使用这片内存,例如一个Uint16Array对象的.buffer属性就是一个ArrayBuffer对象。当Buffer.from函数接收一个ArrayBuffer作为参数时,Node会创建一个新的Buffer对象,不过这个Buffer对象指向的内容还是原来ArrayBuffer的内容,没有任何的数据拷贝行为。我们来看个例子:

const arr = new Uint16Array(2)

arr[0] = 5000
arr[1] = 4000

const buf = Buffer.from(arr.buffer)

console.log(buf)
// Prints: <Buffer 88 13 a0 0f>

// 改变原来数组的数字
arr[1] = 6000

console.log(buf)
// Prints: <Buffer 88 13 70 17>

从上面例子的输出我们可以知道,arrbuf对象会共用同一片内存空间,所以当我们改变原数组的数据时,buf的数据也会发生相应的变化。

其它Buffer操作

看完了创建Buffer的几种做法,我们接着来看一下Buffer其它的一些常用API或者属性

buf.length

这个函数会返回当前buffer占用了多少字节

// 创建一个大小为1234字节的Buffer对象
const buf1 = Buffer.alloc(1234)
console.log(buf1.length)
// Prints: 1234

const buf2 = Buffer.from(&#39;Hello&#39;)
console.log(buf2.length)
// Prints: 5

Buffer.poolSize

这个字段表示Node会为我们预创建的Buffer池子有多大,它的默认值是8192,也就是8KB。Node在启动的时候,它会为我们预创建一个8KB大小的内存池,当用户用某些API(例如Buffer.alloc)创建Buffer实例的时候可能会用到这个预创建的内存池以提高效率,下面是一个具体的例子:

const buf1 = Buffer.from(&#39;Hello&#39;)
console.log(buf1.length)
// Prints: 5

// buf1的buffer属性会指向其底层的ArrayBuffer对象对应的内存
console.log(buf1.buffer.byteLength)
// Prints: 8192

const buf2 = Buffer.from(&#39;World&#39;)
console.log(buf2.length)
// Prints: 5

// buf2的buffer属性会指向其底层的ArrayBuffer对象对应的内存
console.log(buf2.buffer.byteLength)
// Prints: 8192

在上面的例子中,buf1buf2对象由于长度都比较小所以会直接使用预创建的8KB内存池。其在内存的大概表示如图:截屏2022-12-11 下午1.51.54.png这里值得一提的是只有当需要分配的内存区域小于4KB(8KB的一半)并且现有的Buffer池子还够用的时候,新建的Buffer才会直接使用当前的池子,否则Node会新建一个新的8KB的池子或者直接在内存里面分配一个区域(FastBuffer)。

buf.write(string[, offset,[, length]][, encoding])

这个函数可以按照一定的偏移量(offset)往一个Buffer实例里面写入一定长度(length)的数据。我们来看一下具体的例子:

const buf = Buffer.from(&#39;Hello&#39;)

console.log(buf.toString())
// Prints: "Hello"

// 从第3个位置开始写入&#39;LLO&#39;字符
buf.write(&#39;LLO&#39;, 2)
console.log("HeLLO")
// Prints: "HeLLO"

这里需要注意的是当我们需要写入的字符串的长度超过buffer所能容纳的最长字符长度(buf.length)时,超过长度的字符会被丢弃:

const buf = Buffer.from(&#39;Hello&#39;)

buf.write(&#39;LLO!&#39;, 2)
console.log(buf.toString())
// Print:s "HeLLO"

另外,当我们写入的字符长度超过buffer的最长长度,并且最后一个可以写入的字符不能全部填满时,最后一个字符整个不写入:

const buf = Buffer.from(&#39;Hello&#39;)

buf.write(&#39;LL你&#39;, 2)
console.log(buf.toString())
// Prints "HeLLo"

在上面的例子中,由于"你"是中文字符,需要占用三个字节,所以不能全部塞进buf里面,因此整个字符的三个字节都被丢弃了,buf对象的最后一个字节还是保持"o"不变。

Buffer.concat(list[, totalLength])

这个函数可以用来拼接多个Buffer对象生成一个新的buffer。函数的第一个参数是待拼接的Buffer数组,第二个参数表示拼接完的buffer的长度是多少(totalLength)。下面是一个简单的例子:

const buf1 = Buffer.from(&#39;Hello&#39;)
const buf2 = Buffer.from(&#39;World&#39;)

const buf = Buffer.concat([buf1, buf2])
console.log(buf.toString())
// Prints "HelloWorld"

上面的例子中,因为我们没有指定最终生成Buffer对象的长度,所以Node会计算出一个默认值,那就是buf.totalLength = buf1.length + buf2.length。而如果我们指定了totalLength的值的话,当这个值比buf1.lengh + buf2.length小时,Node会截断最后生成的buffer;如果指定的值比buf1.length + buf2.length大时,生成buf对象的长度还是totalLength,多出来的位数填充的内容是0。

这里还有一点值得指出的是,Buffer.concat最后拼接出来的Buffer对象是通过拷贝原来Buffer对象得出来,所以改变原来的Buffer对象的内容不会影响到生成的Buffer对象,不过这里我们还是需要考虑拷贝的性能问题就是了。

Buffer オブジェクトのガベージ コレクション

記事の冒頭で、Node 内のすべての Buffer オブジェクトによって割り当てられるメモリ領域は V8 から独立していると述べました。 ヒープ領域は、オフヒープメモリに属します。これは、Buffer オブジェクトが V8 ガベージ コレクション メカニズム の影響を受けず、手動でメモリを管理する必要があるということですか?実際にはいいえ、Node の API を使用して新しい Buffer オブジェクトを作成するたびに、各 Buffer オブジェクトは JavaScript 空間のオブジェクト (バッファ メモリへの参照) に対応します。このオブジェクトは V8 ガベージ コレクションによって制御されます。そして、Node が必要とするのは以下だけです。この reference がガベージ コレクションされるときに、いくつかのフックをハングして、バッファーが指すオフヒープ メモリを解放します。 簡単に言うと、Buffer によって割り当てられたスペースについて心配する必要はありません。V8 のガベージ コレクション メカニズムは、無駄なメモリを再利用するのに役立ちます。

概要

この記事では、Buffer の一般的な API やプロパティなど、Buffer の基本的な知識を紹介しました。この知識があなたの仕事に役立つことを願っています。 。 ヘルプ。

ノード関連の知識の詳細については、nodejs チュートリアル を参照してください。

以上がこの記事では、Node の Buffer クラスについて詳しく説明します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。