本章的重点是操纵JSON数据、管理二进制数据缓冲区,并实现可读取和可写入输入流及数据压缩/解压缩。
1. 处理JSON
实现Node.js Web应用程序和服务时你将最常使用的一种数据类型JSON(JavaScript Object Notation, Javascript对象符号)。JSON是一个非常轻量级的方法,它用来把Javascript对象和字符串的形式进行互相转换。当你需要序列化数据对象,以便将它们从客户端传递到服务器,从一个进程传递到另一个进程,从一个流传递到另一个流,或当你要将它们存储在数据库中时,使用JSON的效果很好。
相比XML的几个有点:
- JSON更高效,需要更少的字符
- 序列化/反序列化JSON要比序列化/反序列化XML快
- 从开发人员的角度来看,JSON更容易阅读,因为它的语法类似于Javascript
1.1 把JSON转换成Javascript对象
1
2 1JSON.parse(string)
2
1
2
3
4
5
6
7 1var accountStr = '{"name": "Jedi", "members": ["Yoda", "Obi Wan"], "number": 34512, "location": "A galaxy ' +
2 'far, far away"}' ;
3var accountObj = JSON.parse( accountStr ) ;
4
5console.log( accountObj.name ) ;
6console.log( accountObj.members ) ;
7
1.2 把Javascript对象转换为JSON
1
2 1JSON.stringify()
2
1
2
3
4
5
6
7
8
9
10
11 1var accountObj = {
2 name: 'Baggins',
3 number: 10645,
4 members: ['Frodo, Bilbo'],
5 location: 'Shire'
6} ;
7
8var accountStr = JSON.stringify( accountObj ) ;
9
10console.log( accountStr ) ;
11
2. 使用Buffer模块缓冲数据
虽然Javascript可能是对Unicode是友好的,但是它不很擅长管理二进制数据。然而,在实施一些Web应用程序和服务时,二进制数据是非常有用的。
- 传输压缩文件
- 生成动态图像
- 发送序列化的二进制数据
2.1 了解缓冲数据
缓冲数据是由一系列的大端或小端格式字节组成的。这意味着它们比文本数据占用比较少的空间。
Node.js提供Buffer模块,它允许你在缓冲去结构中创建、读取、写入和操作二进制数据。Buffer模块是全局性的,所以不需要require()函数来访问它。
缓存数据被存储在正常V8堆之外的原始内存分配区中,因此,缓冲区不能调整大小。
缓冲区与字符串进行互相转换时,需要指定要使用的明确的编码方法。
utf8
多字节编码的Unicode字符,是大多数文档和网页中的标砖
utf16le
2个或4个字节小端编码的Unicode字符
usc2
2个或4个字节小端编码的Unicode字符
base64
Base-64字符串编码
Hex
每个字节编码为两个十六进制字符
2.2 创建缓冲区
Buffer对象实际上是原始的内存分配区。因此,你必须在创建时确定其大小。
使用new关键字创建Buffer对象有3中方法:
1
2
3
4 1new Buffer(sizeInBytes)
2new Buffer(octetArray)
3new Buffer(string, [encoding])
4
1
2
3
4 1var buf256 = new Buffer(256) ;
2var bufOctet8 = new Buffer([0x6f, 0x63, 0x74, 0x65, 0x74, 0x73]) ;
3var bufUTF8 = new Buffer('Some UTF8 Text \u00b6 \u30c6 \u20ac', 'utf8') ;
4
2.3 写入缓冲区
Buffer对象已经创建后,你不能扩展其大小,但可以把数据写到缓冲区中的任何位置。
buffer.write(string, [offset], [length], [encoding])
使用encoding的编码从缓冲区内的offset(偏移量)索引开始。写入string中length数量的字节
buffer[offset] = value
将索引offset处的数据替换为指定的value
buffer.fill(value, [offset], [end])
将value写到缓冲区中从offset索引处开始,并在end索引处结束的每一个字节
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 1buf256 = new Buffer( 256 ) ;
2
3buf256.fill( 0 ) ;
4buf256.write( 'add some text' ) ;
5
6console.log( buf256.toString() ) ;
7
8buf256.write( 'more text', 9, 9 ) ;
9
10console.log( buf256.toString() ) ;
11
12buf256[ 18 ] = 43 ;
13
14console.log( buf256.toString() ) ;
15
16
2.4 从缓冲区读取
buffer.toString([encoding], [start], [end])
返回一个字符串,它包含了从缓冲区的start索引到end索引的字符,由encoding指定的编码解码。如果没有指定start或end,则toString()使用缓冲区的开始或结束
stringDecoder.write(buffer)
返回缓冲区的解码字符串版本
buffer[offset]
返回缓冲区在指定的offset字节的八进制值
1
2
3
4
5
6
7
8
9 1var bufUTF8 = new Buffer('Some UTF8 Text \u00b6 \u30c6 \u20ac', 'utf8') ;
2console.log(bufUTF8.toString()) ;
3console.log(bufUTF8.toString('utf8', 5, 9)) ;
4var StringDecoder = require('string_decoder').StringDecoder ;
5var decoder = new StringDecoder('utf8') ;
6console.log(decoder.write(bufUTF8)) ;
7console.log(bufUTF8[18].toString(16)) ;
8console.log(bufUTF8.readUInt32BE(18).toString(16)) ;
9
2.5 确定缓冲区长度
-
在Buffer对象上调用.length来确定缓冲区的长度
-
字符串在缓冲区中占用的字节长度,用Buffeer.byteLength(string, [encoding])
-
缓冲区中字符串长度和字节长度之间的区别很重要
1
2
3
4
5
6
7 1console.log('UTF8 text \u00b6'.length) ;
2// 11
3console.log(Buffer.byteLength('UTF8 text \u00b6', 'utf8')) ;
4// 12
5console.log(Buffer('UTF8 text \u00b6').length) ;
6// 12
7
2.6 复制缓冲区
1
2 1copy(targetBuffer, [targetStart], [sourceStart], [sourceIndex])
2
注意:若要从一个缓冲区复制字符串数据到另一个缓冲区,应确保两个缓冲区使用相同的编码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 1/**
2 * Created by 23782 on 2016/4/26.
3 */
4var alphabet = new Buffer('abcdefghigklmnopqrstuvwxyz') ;
5console.log(alphabet.toString()) ;
6// copy full buffer
7var blank = new Buffer(26) ;
8blank.fill() ;
9console.log('Blank: ' + blank.toString()) ;
10alphabet.copy(blank) ;
11console.log('Blank: ' + blank.toString()) ;
12// copy part of buffer
13var dashes = new Buffer(26) ;
14dashes.fill('-') ;
15console.log('Dashes: ' + dashes.toString()) ;
16alphabet.copy(dashes, 10, 10, 15) ;
17console.log('Dashes: ' + dashes.toString()) ;
18// copy to and from direct indexes of buffers
19var dots = new Buffer('----------------------------') ;
20dots.fill('.') ;
21console.log('dots: ' + dots.toString()) ;
22for(var i = 0; i < dots.length; i++) {
23 if (i % 2) dots[i] = alphabet[i] ;
24}
25console.log('dots: ' + dots.toString()) ;
26
2.7 对缓冲区切块
处理缓冲区的另一个重要方面是将它们分成切片的功能。
切片(slice):是缓冲区的开始索引和结束索引之间的部分。对缓冲区切片可以让你操作一个特定的块。
1
2 1slice([start], [end])
2
返回一个Buffer对象,其指向原缓冲区的start索引,并具有end – start的长度。
注意:切片和副本不同,如果你编辑一个副本,原来的缓冲区并没有改变。但是,如果你编辑一个切片,则原来的缓冲区确实会改变。
1
2
3
4
5
6
7
8
9 1var numbers = new Buffer('123456789') ;
2console.log(numbers.toString()) ;
3var slice = numbers.slice(3, 6) ;
4console.log(slice.toString()) ;
5slice[0] = '#'.charCodeAt(0) ;
6slice[slice.length - 1] = '#'.charCodeAt(0) ;
7console.log(slice.toString()) ;
8console.log(numbers.toString()) ;
9
2.8 拼接缓冲区
把两个或多个buffer对象拼接在一起,形成一个新的缓冲区。
1
2 1concat(list, [totalLength])
2
该方法接受buffer对象的数组作为第一个参数,并把定义缓冲区最大字节数的totalLength作为可选的第二个参数。
如果不提供totalLength参数,cancat()就为你计算出总长度。这样选哦遍历列表,所以提供totalLength值执行的更快一点。
1
2
3
4
5
6 1var af = new Buffer('African Swallow?') ;
2var eu = new Buffer('European Swallow?') ;
3var question = new Buffer('Air Speed Velocity of an ') ;
4console.log(Buffer.concat([question, af]).toString()) ;
5console.log(Buffer.concat([question, eu]).toString()) ;
6