Node.js中有一个Buffer类,必须要介绍一下,因为我们在使用Node.js做服务端开发时,http、tcp、udp、文件io等等类型的操作,都会用到Buffer,离开它基本没办法玩儿下去。
JavaScript里的String对象,存储的是字符串,而且是Unicode编码的。
Buffer代表一个缓冲区,存储二进制数据,是字节流。我们在网络传输时,就传输的这种字节流。写文件时,也是写的字节流。
字符串是有编码格式的,比如UTF-8。而Buffer是没有编码格式的。两者可以相互转换。转换时必须指定编码格式。
我们之前已经用过的http模块(参看Node.js开发入门——HTTP文件服务器和Node.js开发入门——HelloWorld再分析),http.createServer方法需要的回调函数的原型是:
function (req, res)
这个回调的第一个参数,req,类型是http.IncomingMessage,而http.IncomingMessage是一个只读的流,实现了Readable接口,stream.Readable读到的数据(监听data事件可以处理),就是Buffer对象,是字节流。而我们在程序中使用时,经常是要转换为String。反过来,res(类型http.ServerResponse,可写的流,实现了Writable接口)有个方法setDefaultEncoding,用来设置流的编码格式,在write数据时,会使用指定的编码格式来编码数据,然后发送给客户端。
就是说,网络传输的是Buffer,程序需要处理String,Buffer和String之间可以转换。Buffer有toString方法,可以按指定的编码格式将字节流转换为String。
还有,文件系统模块(参看Node.js开发入门——使用http访问外部世界或Node.js开发入门——HTTP文件服务器),fs.createWriteStream和fs.createReadStream两个方法都有一个可选参数options,可以指定defaultEncoding,这里指定的编码格式,也是用于在Buffer和String之间转换的。
目前我们在Node.js里,Buffer在转换为字符串时,toString方法的第一个参数就是编码类型,支持常见的编码格式:
假如你不确认某个编码格式是否正确,可以使用Buffer.isEncoding(encoding)方法来测试。
在使用Buffer的toString方法时,如果你不指定编码格式,则默认使用utf8来转换。toString原型:
buf.toString([encoding][, start][, end])
第一个参数是编码格式,第二个是开始位置(0到buf.length-1),第三个是结束位置(不包含这个索引位置的数据)。
使用new操作符,有四种方法创建一个Buffer实例:
比如下面的代码可以创建Buffer的实例:
var buf1 = new Buffer(256);
var buf2 = new Buffer("Hello Buffer");
var buf3 = new Buffer([0x65,0x66,0x67]);
var buf4 = new Buffer(buf2);
有一点需要说明,使用new Buffer(size)分配的缓冲区,是未初始化的哦。那块内存里,可能是脏的,什么玩意儿都有。试试下面的代码:
var buf1 = new Buffer(256);
buf1.write('abc');
console.log("buf1\'s content: ", buf1.toString());
上面的代码企图使用toString转换Buffer,可是你会看到控制台输出了很多乱码……修改一下,使用buf.fill()方法填充一下就好了:
var buf1 = new Buffer(256);
buf1.fill(0);
buf1.write('abc');
console.log("buf1\'s content: ", buf1.toString());
字符串是“\0”结尾的,调用了buf1.fill(0)之后,就一切安好。
前面我们已经使用了buf.write方法来向缓冲区里写入数据了。write的原型如下:
buf.write(string[, offset][, length][, encoding])
buf.write用来向缓冲区中写入一个字符串,返回实际写入的字节数。参数含义如下:
Buffer还有很多其他的方法,让你操作缓冲区。比如writeUInt8、writeUInt16LE、writeUInt16BE、writeUInt31LE、writeUInt32BE、writeInt8、writeInt16LE、writeInt32LE等等。
LE - little endian,小端字节序。
BE - big endian,大端字节序,即网络字节序。
需要说明的是,writeX方法,不像文件一样自动为你保存当前位置哦,你不指定offset,它就总是从0位置开始写……
其它的,看文档吧……
buf.toString其实是一种读数据的方式。当然,还有其它的,比如buf[index]可以根据下标读取字节,buf.readIntXXX,buf.readUIntXXX……
buf.toJSON()可以把一个Buffer对象转换为JSON格式。当你针对一个Buffer对象调用JSON.stringify方法时,buf.toJSON()就会被调用。比如:
var buf = new Buffer('test');
var json = JSON.stringify(buf);
console.log(json);
// '{"type":"Buffer","data":[116,101,115,116]}'
一个Buffer对象的大小,在创建时就固定下来,创建之后不可改变。嘿嘿,这可是容易引起误解的,尤其是你觉得Buffer里保存的是字符串时。试试下面的代码有助于理解这一点:
var buf1 = new Buffer(256);
buf1.fill(0);
buf1.write('abc');
console.log("buf1\'s length - %d, not 3\n", buf1.length);
buf1.write('abcdef');
console.log("buf1\'s length - %d, not 6\n", buf1.length);
另外当你要从确定字符串在缓冲区中占用的字节长度时,不能使用字符串的length属性,因为String.length返回的是字符长度。而对于采用UTF8等编码格式编码的字符串,一个字符可能占用多个字节。所以,String.length所代表的字符串长度和字节长度就不一致。注意,Buffer.length返回的是缓冲区的字节长度,而且是创建时的那个长度,不会随着缓冲内容变化而变化。
要想衡量一个字符串占用的字节长度,可以使用Buffer.byteLength(string[,encoding])这个方法,它会测量一个字符串在指定编码格式下占用的字节长度。
来看一个简单的例子:
var name = new String('who is \u5F20\u4E09\u4E30?');
console.log('name.length = %d', name.length);
console.log('byteLength = %d', Buffer.byteLength(name, 'utf8'));
Buffer还支持切片、拷贝、拼接、比较等操作。
buf.slice([start[, end]])可以根据起止位置(不包含结束位置对应的数据)对一个缓冲区进行切片,返回一个新的Buffer对象,方便我们操作缓冲区的某个区域。但值得注意的是,这个切片是对原有缓冲区的引用,而不是副本,你对切片内容的修改,实际上修改的是原始的缓冲区。这个方法返回一个代表切片的Buffer对象。
buf.copy(targetBuffer[, targetStart][, sourceStart][, sourceEnd])可以将一个缓冲区指定区域的内容拷贝到另一个缓冲的指定区域。类似C语言里的memcpy。targetStart指定目标缓冲区的起始偏移,sourceStart指定源缓冲区的起始偏移,它们默认都是0;sourceEnd指定源缓冲区的结束位置,默认是源缓冲区的长度。实际复制时,会比较目标缓冲区的长度和待复制区域的长度,哪个小按哪个来,不会越界。
Buffer有一个类方法,concat(list[,totalLength]),可以将一串缓冲区拼接成一个。第一个参数list是一个缓冲区数组,第二是待拼接的缓冲区的总长度。如果你不提供totalLength,concat会自己遍历list中的缓冲区计算总长度,会有一点性能损失。这个方法返回拼接后的缓冲区。
看个示例代码,演示切片、拷贝和拼接的用法:
var buf1 = new Buffer('1234');
var buf2 = new Buffer('12567');
var bufList = [buf1, buf2];
var buf3 = Buffer.concat(bufList);
console.log('buf3 - %s', buf3.toString());
var buf4 = buf3.slice(3, 8);
console.log('buf4 - %s', buf4.toString());
var buf5 = new Buffer(5);
buf3.copy(buf5, 0, 1);
console.log('buf5 - %s', buf5.toString());
buf.equals(otherBuffer)判断当前缓冲区是否和另一个相等,相等时返回true。
buf.compare(otherBuffer)比较当前缓冲区和另一个缓冲区的大小,相等返回0,小于返回-1,大于返回1。看下面的示例代码:
var buf1 = new Buffer('1234');
var buf2 = new Buffer('12567');
var buf3 = new Buffer('1234');
var buf4 = new Buffer('0123');
console.log('buf1.compare(buf2) = ', buf1.compare(buf2));
console.log('buf1.compare(buf3) = ', buf1.compare(buf3));
console.log('buf1.compare(buf4) = ', buf1.compare(buf4));
其它文章: