字符是如何存储的?这取决于编码。不同的字符对应不同的编码,然后在需要渲染的时候,根据对应的编码去查字体库,然后渲染出对应字符的图形。字符集字符集(charset)最初是ASCII码,即128个字符如abcABC123,因为计算机最早是在美国发明的。后来欧洲也制定了一套字符集标准叫ISO,后来中国也制定了一套叫GBK。国际标准化组织认为不可能单独制定一套,否则同一个编码在不同的字符集下会有不同的含义,所以提出了unicode编码,其中包含了世界上大部分的编码,所以每个字符只有一个唯一的代码。但是ASCII码只需要1个字节存储,而GBK需要2个字节,有的字符集需要3个字节等等,有的只需要一个字节存储却存储了2个字节,很浪费空间。于是就有了utf-8、utf-16、utf-24等不同的编码方案。utf-8、utf-16、utf-24都是unicode编码,只是具体实现不同而已。为了节省空间,UTF-8设计了从1到6字节的变长存储方案。UTF-16是固定的2个字节,而UTF-24是固定的4个字节。最后,UTF-8被广泛使用是因为它占用的空间最少。Node.js中Buffer的编码支持各种语言的字符集的编码和解码,Node.js也是一样的。在Node.js中,Buffer可以用来存储二进制数据,二进制数据转换成字符串时,需要指定一个字符集。Buffer的from、byteLength、lastIndexOf等方法都支持指定编码:具体支持的编码如下:utf8、ucs2、utf16le、latin1、ascii、base64、hex有同学可能会发现:base64、hex不是字符集,为什么会出现在这里?是的,字节转字符的编码方案除了字符集,还有转为明文字符的base64,转为十六进制的hex。这就是为什么Node.js称其为encoding而不是charset的原因,因为支持的编解码方案不仅仅是字符集。如果未指定编码,则默认为utf8。constbuf=Buffer.alloc(11,'aGVsbG8gd29ybGQ=','base64');console.log(buf.toString());//helloworldencoding的源码关于编码我去查了下Node.js的源码:这段是encoding的实现:https://github.com/nodejs/node/blob/master/lib/buffer.js#L587-L726可以看到每个encoding都实现了encoding,encodingVal,byteLength,write,slice,indexOf几个API,因为这些API使用不同的编码方案,所以会有不同的结果。Node.js会根据传入的编码返回不同的对象。这是一个多态的想法。constencodingOps={utf8:{encoding:'utf8',encodingVal:encodingsMap.utf8,byteLength:byteLengthUtf8,write:(buf,string,offset,len)=>buf.utf8Write(string,offset,len),切片:(buf,start,end)=>buf.utf8Slice(start,end),indexOf:(buf,val,byteOffset,dir)=>indexOfString(buf,val,byteOffset,encodingsMap.utf8,dir)},ucs2:{encoding:'ucs2',encodingVal:encodingsMap.utf16le,byteLength:(string)=>string.length*2,write:(buf,string,offset,len)=>buf.ucs2Write(string,offset,len),slice:(buf,start,end)=>buf.ucs2Slice(start,end),indexOf:(buf,val,byteOffset,dir)=>indexOfString(buf,val,byteOffset,encodingsMap.utf16le,dir)},utf16le:{encoding:'utf16le',encodingVal:encodingsMap.utf16le,byteLength:(string)=>string.length*2,write:(buf,string,offset,len)=>buf.ucs2Write(string,offset,len),slice:(buf,start,end)=>buf.ucs2Slice(start,end),indexOf:(buf,val,byteOffset,dir)=>indexOfString(buf,val,byteOffset,encodingsMap.utf16le,dir)},latin1:{编码:'latin1',encodingVal:encodingsMap.latin1,byteLength:(string)=>string.length,write:(buf,string,offset,len)=>buf.latin1Write(string,offset,len),slice:(buf,start,end)=>buf.latin1Slice(start,end),indexOf:(buf,val,byteOffset,dir)=>indexOfString(buf,val,byteOffset,encodingsMap.latin1,dir)},ascii:{encoding:'ascii',encodingVal:encodingsMap.ascii,byteLength:(string)=>string.length,write:(buf,string,offset,len)=>buf.asciiWrite(string,offset,len),slice:(buf,start,end)=>buf.asciiSlice(start,end),indexOf:(buf,val,byteOffset,dir)=>indexOfBuffer(buf,fromStringFast(val,encodingOps.ascii),byteOffset,encodingsMap.ascii,dir)},base64:{编码:'base64',encodingVal:encodingsMap.base64,byteLength:(string)=>base64ByteLength(string,string.length),write:(buf,string,offset,len)=>buf.base64Write(string,offset,len),slice:(buf,start,end)=>buf.base64Slice(start,end),indexOf:(buf,val,byteOffset,dir)=>indexOfBuffer(buf,fromStringFast(val,encodingOps.b)ase64),byteOffset,encodingsMap.base64,dir)},hex:{encoding:'hex',encodingVal:encodingsMap.hex,byteLength:(string)=>string.length>>>1,write:(buf,string,offset,len)=>buf.hexWrite(string,offset,len),slice:(buf,start,end)=>buf.hexSlice(start,end),indexOf:(buf,val,byteOffset,dir)=>indexOfBuffer(buf,fromStringFast(val,encodingOps.hex),byteOffset,encodingsMap.hex,dir)}};functiongetEncodingOps(encoding){encoding+='';switch(encoding.length){case4:if(encoding==='utf8')returnencodingOps.utf8;if(encoding==='ucs2')returnencodingOps.ucs2;encoding=StringPrototypeToLowerCase(encoding);if(encoding==='utf8')returnencodingOps.utf8;if(encoding==='ucs2')returnencodingOps.ucs2;break;case5:if(encoding==='utf-8')returnencodingOps.utf8;if(encoding==='ascii')returnencodingOps.ascii;if(encoding==='ucs-2')returnencodingOps.ucs2;encoding=StringPrototypeToLowerCase(encoding);if(encoding==='utf-8')returnencodingOps.utf8;if(encoding==='ascii')returnencodingOps.ascii;if(encoding==='ucs-2')returnencodingOps.ucs2;break;case7:if(encoding==='utf16le'||StringPrototypeToLowerCase(encoding)==='utf16le')returnencodingOps.utf16le;break;case8:if(encoding==='utf-16le'||StringPrototypeToLowerCase(encoding)==='utf-16le')返回encodingOps.utf16le;break;case6:if(encoding==='latin1'||encoding==='binary')returnencodingOps.latin1;if(encoding==='base64')returnencodingOps.base64;encoding=StringPrototypeToLowerCase(encoding);if(encoding==='latin1'||encoding==='二进制')returnencodingOps.latin1;if(encoding==='base64')returnencodingOps.base64;break;case3:if(encoding==='hex'||StringPrototypeToLowerCase(encoding)==='hex')returnencodingOps.hex;break;}}总结计算机中存储数据的最小单位是比特,而存储信息的最小单位是字节。基于编码与字符的映射关系,实现了各种字符集,包括ascii、iso、gbk等,而国际标准化组织则提出unicode包含所有字符。有几种unicode实现:utf-8、utf-16和utf-32。它们使用不同数量的字节来存储字符。Utf-8是变长的,而且存储量最小,所以应用广泛。Node.js通过Buffer存储二进制数据,转换为字符串时需要指定编码方案。这种编码方案不仅包括字符集(charset),还支持hex和base64方案,包括:utf8、ucs2、utf16le、latin1、Ascii、base64、hex我们查看了编码Node.js的源码,发现每一种编码方案都会实现一系列的API,这是一种多态的思想。
