收录了这篇文章
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
在引入 TypedArray 之前,JavaScript 语言没有用于读取或操作二进制数据流的机制。 Buffer 类是作为 Node.js API 的一部分引入的,用于在 TCP 流、文件系统操作、以及其他上下文中与八位字节流进行交互。
现在可以使用 TypedArray, Buffer 类以更优化和更适合 Node.js 的方式实现了 Uint8Array API。
Buffer 类的实例类似于从 0 到 255 之间的整数数组(其他整数会通过 & 255 操作强制转换到此范围),但对应于 V8 堆外部的固定大小的原始内存分配。 Buffer 的大小在创建时确定,且无法更改。
Buffer 类在全局作用域中,因此无需使用 require('buffer').Buffer。
创建Buffer:
// 创建一个长度为 10、且用零填充的 Buffer。 const buf1 = Buffer.alloc(10); // 创建一个长度为 10、且用 0x1 填充的 Buffer。 const buf2 = Buffer.alloc(10, 1); // 创建一个长度为 10、且未初始化的 Buffer。 // 这个方法比调用 Buffer.alloc() 更快, // 但返回的 Buffer 实例可能包含旧数据, // 因此需要使用 fill() 或 write() 重写。 const buf3 = Buffer.allocUnsafe(10); // 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer。 const buf4 = Buffer.from([1, 2, 3]); // 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。 const buf5 = Buffer.from('tést'); // 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。 const buf6 = Buffer.from('tést', 'latin1');
Buffer 与字符编码:
当字符串数据被存储入 Buffer 实例或从 Buffer 实例中被提取时,可以指定一个字符编码。
const buf = Buffer.from('hello world', 'ascii'); console.log(buf.toString('hex')); // 打印: 68656c6c6f20776f726c64 console.log(buf.toString('base64')); // 打印: aGVsbG8gd29ybGQ= console.log(Buffer.from('fhqwhgads', 'ascii')); // 打印: <Buffer 66 68 71 77 68 67 61 64 73> console.log(Buffer.from('fhqwhgads', 'utf16le')); // 打印: <Buffer 66 00 68 00 71 00 77 00 68 00 67 00 61 00 64 00 73 00>
Node.js 当前支持的字符编码有:
'ascii' - 仅适用于 7 位 ASCII 数据。此编码速度很快,如果设置则会剥离高位。
'utf8' - 多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8。
'utf16le' - 2 或 4 个字节,小端序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)。
'ucs2' - 'utf16le' 的别名。
'base64' - Base64 编码。当从字符串创建 Buffer 时,此编码也会正确地接受 RFC 4648 第 5 节中指定的 “URL 和文件名安全字母”。
'latin1' - 一种将 Buffer 编码成单字节编码字符串的方法(由 RFC 1345 中的 IANA 定义,第 63 页,作为 Latin-1 的补充块和 C0/C1 控制码)。
'binary' - 'latin1' 的别名。
'hex' - 将每个字节编码成两个十六进制的字符。
现代的 Web 浏览器遵循 WHATWG 编码标准,将 'latin1' 和 'ISO-8859-1' 别名为 'win-1252'。 这意味着当执行 http.get() 之类的操作时,如果返回的字符集是 WHATWG 规范中列出的字符集之一,则服务器可能实际返回 'win-1252' 编码的数据,而使用 'latin1' 编码可能错误地解码字符。
Buffer(数据块)语法
JS 语言自身只有字符串数据类型,没有二进制数据类型,因此 NodeJS 提供了一个与 String 对等的全局构造函数 Buffer 来提供对二进制数据的操作。除了可以读取文件得到 Buffer 的实例外,还能够直接构造,例如:
var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);
Buffer 与字符串类似,除了可以用.length属性得到字节长度外,还可以用[index]方式读取指定位置的字节,例如:
bin[0]; // => 0x68;
Buffer 与字符串能够互相转化,例如可以使用指定编码将二进制数据转化为字符串:
var str = bin.toString('utf-8'); // => "hello"
或者反过来,将字符串转换为指定编码下的二进制数据:
var bin = new Buffer('hello', 'utf-8'); // => <Buffer 68 65 6c 6c 6f>
Buffer 与字符串有一个重要区别。字符串是只读的,并且对字符串的任何修改得到的都是一个新字符串,原字符串保持不变。至于 Buffer,更像是可以做指针操作的 C 语言数组。例如,可以用[index]方式直接修改某个位置的字节。
bin[0] = 0x48;
而.slice方法也不是返回一个新的 Buffer,而更像是返回了指向原 Buffer 中间的某个位置的指针,如下所示。
[ 0x68, 0x65, 0x6c, 0x6c, 0x6f ] ^ ^ | | bin bin.slice(2)
因此对.slice方法返回的 Buffer 的修改会作用于原 Buffer,例如:
var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]); var sub = bin.slice(2); sub[0] = 0x65; console.log(bin); // => <Buffer 68 65 65 6c 6f>
也因此,如果想要拷贝一份 Buffer,得首先创建一个新的 Buffer,并通过.copy方法把原 Buffer 中的数据复制过去。这个类似于申请一块新的内存,并把已有内存中的数据复制过去。以下是一个例子。
var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]); var dup = new Buffer(bin.length); bin.copy(dup); dup[0] = 0x48; console.log(bin); // => <Buffer 68 65 6c 6c 6f> console.log(dup); // => <Buffer 48 65 65 6c 6f>
总之,Buffer 将 JS 的数据处理能力从字符串扩展到了任意二进制数据。
参考:
《Node.js开发指南》
http://nodejs.org/api/buffer.html
https://www.runoob.com/nodejs/nodejs-buffer.html
http://nodejs.cn/api/buffer.html