javascript程序是使用Unicode
字符集编写的。Unicode
是ASCII
和Latin-1
的超集,并支持地球上几乎全部的语言。ECMAScript3
要求JavaScript必须支持Unicode2.1
及后续版本,ECMAScript5
则要求支持Unicode3
及后续版本。因此,咱们编写出来的javascript程序,都是使用Unicode编码的。javascript
UTF-8(UTF8-bit Unicode Transformation Format)
是一种针对Unicode的可变长度字符编码,也是一种前缀码。java
它能够用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容
,这使得原来处理ASCII字符的软件无须或只须作少部分修改,便可继续使用。所以,它逐渐成为电子邮件、网页及其余存储或发送文字的应用中,优先采用的编码
。node
目前大部分的网站,都是使用的UTF-8编码。数组
如标题所说的应用场景十分常见,例如发送一段二进制到服务器时,服务器规定该二进制内容的编码必须为UTF-8。这种状况下,咱们必须就要经过程序将javascript的Unicode字符串转为UTF-8编码的字符串。浏览器
转换以前咱们必须了解Unicode的编码结构是固定的。
不信能够试一试 String 的 charCodeAt 这个方法,看看返回的 charCode 占几个字节。服务器
- 英文占1个字符,汉字占2个字符
然而,UTF-8的编码结构长度是根据某单个字符的大小
来决定长度有多少。
下面为单个字符的大小占用几个字节。单个unicode字符编码以后的最大长度为6个字节。函数
- 1个字节:Unicode码为0 - 127
- 2个字节:Unicode码为128 - 2047
- 3个字节:Unicode码为2048 - 0xFFFF
- 4个字节:Unicode码为65536 - 0x1FFFFF
- 5个字节:Unicode码为0x200000 - 0x3FFFFFF
- 6个字节:Unicode码为0x4000000 - 0x7FFFFFFF
具体请看图片:
网站
由于英文和英文字符的Unicode码为0 - 127
,因此英文在Unicode和UTF-8中的长度和字节都是一致的,只占用1个字节。这也就是为何UTF8是Unicode的超集
!this
如今咱们再来讨论汉字,由于汉字的unicode码区间为0x2e80 - 0x9fff
, 因此汉字在UTF8中的长度最长为3个字节。编码
那么汉字是如何从Unicode的2个字节转换为UTF8的三个字节的哪?
假设我须要把汉字"中"转为UTF-8的编码
var str = '中'; var charCode = str.charCodeAt(0); console.log(charCode); // => 20013
由上一步咱们获得汉字"中"的charCode为20013.而后咱们发现20013位于2048 - 0xFFFF这个区间里,因此汉字"中"应该在UTF8中占3个字节。
既然知道汉字"中"须要占3个字节,那么这3个字节如何获得哪?
这就须要设计到补码,具体补码逻辑以下:
好吧,我知道这个图大家也看不明白,仍是我来说吧!
具体的补位码以下,"x"表示空位,用来补位的。
- 0xxxxxxx
- 110xxxxx 10xxxxxx
- 1110xxxx 10xxxxxx 10xxxxxx
- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
warning:有没有发现?补位码第一个字节前面有几个1就表示整个UTF-8编码占多少个字节!UTF-8解码为Unicode就是利用的这个特色哦~
咱们先举个简单的例子。把英文字母"A"转为UTF8编码。
一、“A”的charCode为65
二、65位于0-127的区间,因此“A”占一个字节
三、UTF8中一个字节的补位为0xxxxxxx,x表示的是空位,是用来补位的。
四、将65转为二进制获得1000001
五、将1000001按照从前到后的顺序,依次补到0xxxxxxx的空位中,获得01000001
六、将01000001转为字符串,获得"A"
七、最终,"A"为UTF8编码以后“A”
经过这个小例子,咱们是否再次验证了UTF-8是Unicode的超集
!
好了,咱们如今再回到汉字"中"上,以前咱们已经获得了"中"的charCode为20013,二进制为01001110 00101101
。具体以下:
var code = 20013; code.toString(2); // => 100111000101101 等同于 01001110 00101101
而后,咱们按照上面“A”补位的方法,来给"中"补位。
将01001110 00101101
按照从前到后的顺序依此补位到1110xxxx 10xxxxxx 10xxxxxx
上.获得11100100 10111000 10101101
.
经过上面的步骤,咱们获得了"中"的三个UTF8字节,11100100 10111000 10101101
。
咱们将每一个字节转为16进制,获得0xE4 0xB8 0xAD
;
那么这个0xE4 0xB8 0xAD
就是咱们最终获得的UTF8编码了。
咱们使用nodejs的buffer来验证一下是否正确。
var buffer = new Buffer('中'); console.log(buffer.length); // => 3 console.log(buffer); // => <Buffer e4 b8 ad> // 最终获得三个字节 0xe4 0xb8 0xad
由于16进制是不分大小写的,因此是否是跟咱们算出来0xE4 0xB8 0xAD
如出一辙。
// 将字符串格式化为UTF8编码的字节 var writeUTF = function (str, isGetBytes) { var back = []; var byteSize = 0; for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i); if (0x00 <= code && code <= 0x7f) { byteSize += 1; back.push(code); } else if (0x80 <= code && code <= 0x7ff) { byteSize += 2; back.push((192 | (31 & (code >> 6)))); back.push((128 | (63 & code))) } else if ((0x800 <= code && code <= 0xd7ff) || (0xe000 <= code && code <= 0xffff)) { byteSize += 3; back.push((224 | (15 & (code >> 12)))); back.push((128 | (63 & (code >> 6)))); back.push((128 | (63 & code))) } } for (i = 0; i < back.length; i++) { back[i] &= 0xff; } if (isGetBytes) { return back } if (byteSize <= 0xff) { return [0, byteSize].concat(back); } else { return [byteSize >> 8, byteSize & 0xff].concat(back); } } writeUTF('中'); // => [0, 3, 228, 184, 173] // 前两位表示后面utf8字节的长度。由于长度为3,因此前两个字节为`0,3` // 内容为`228, 184, 173`转成16进制就是`0xE4 0xB8 0xAD`
// 读取UTF8编码的字节,并专为Unicode的字符串 var readUTF = function (arr) { if (typeof arr === 'string') { return arr; } var UTF = '', _arr = this.init(arr); for (var i = 0; i < _arr.length; i++) { var one = _arr[i].toString(2), v = one.match(/^1+?(?=0)/); if (v && one.length == 8) { var bytesLength = v[0].length; var store = _arr[i].toString(2).slice(7 - bytesLength); for (var st = 1; st < bytesLength; st++) { store += _arr[st + i].toString(2).slice(2) } UTF += String.fromCharCode(parseInt(store, 2)); i += bytesLength - 1 } else { UTF += String.fromCharCode(_arr[i]) } } return UTF } readUTF([0, 3, 228, 184, 173]); => '中'
另一种比较简单的将中文转为UTF8字节码的方法比较简单,浏览器也提供了一个方法,并且这个方法你们都一直在用,是什么哪?就是encodeURI
。固然,encodeURIComponent
也是能够的。
没错,就是这个方法。那么这个方法是怎么将一个Unicode编码的中文转为UTF8的字节码嘞?
var str = '中'; var code = encodeURI(str); console.log(code); // => %E4%B8%AD
有没有发现获得了一个转义后的字符串,并且这个字符串中的内容和我以前在上面获得的字节码是同样的~~~。
下面咱们将%E4%B8%AD
转为一个number数组。
var codeList = code.split('%'); codeList = codeList.map(item => parseInt(item,16)); console.log(codeList); // => [228, 184, 173]
如此简单,有木有~~~
这里就涉及到的URI
中的querystring
编码的问题了。由于按照规定,URI中的querystring必须按照UTF8的编码进行传输,而JavaScript是Unicode的,因此浏览器就给咱们提供了一个方法,也就是encodeURI
/encodeURIComponent
方法。这个方法会讲非英文字符
(这里考虑下,为何是非英文字符?)先转为UTF8的字节码,而后前面加个%进行拼接,因此咱们将汉字"中"
转义下便获得了"%E4%B8%AD"
.
好吧,原理就这些,没有其余的了。
不过,这种方法还有个缺点,那就是只会转义非英文字符
,因此当咱们须要将英文字符也格式化为UTF8编码时,这个方法是达不到咱们需求的,咱们还须要额外的将英文字符也给转义下。
那我想要解析回来应该怎么作哪?用decodeURI
/decodeURIComponent
就能够了。
var codeList = [228, 184, 173]; var code = codeList.map(item => '%'+item.toString(16)).join(''); decodeURI(code); // => 中
好了,到这里本文也就介绍完UTF8的编码了。 但愿能够帮助你们了解到UTF-8编码的原理。