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編碼的原理。