字符編碼、字符長度錯誤、截取字符錯誤、UTF八、Unicode
計算機重重底層之下都是由 0 和 1 組合,可是你知道他們是怎麼一步步變成字符串的嘛?在咱們現實生活中最多見的例子能夠經過 wo
在新華字典中找到 我
這個字。一樣計算機經過 0 和 1 組合在 字典
中查找到對應的字符,那 字典
內容是什麼呢?javascript
計算機誕生於 美國
它的使用者大多數使用英文,美國國家標準學會
便制定了這本字典
包括了 26個大寫英文字母
、26個小寫英文字母
、10個阿拉伯數字
等總共 256
個字符的 ASCII 字符集。html
ASCII 用二進制來表示就是 0000 0000
到 1111 1111
被用得滿滿當當,漢字就沒有地方能夠放得下了這下怎麼辦?正所謂江山大有人才出,國標編碼 GB
系列出現了,其中最耳熟能詳的就是 GB2312
。前端
那麼問題來了世界擁有 2500
至 3500
種語言,有文字的語言有 930 種。你能想象你在瀏覽不一樣語言界面的時候,須要本身不斷的去切換 字典
而且 每次切換查找不到的字符就會亂碼
出現。java
書同文,車同軌,行同倫。
上面這句話歌頌了秦始王具備跨時代意義的成就,可是現實世界中統一語言顯得不可能。那咱們可否換個思路解決這個問題呢?先思考一個問題:「把大象放入冰箱須要幾步」,答案你們都知道「打開,裝進去,關上」。那統一字符怎麼辦呢?那就是建立一個足夠大的字典
把全部的字符都放進去。git
Unicode 萬國碼 轟隆一聲誕生了,顧名思義統一了全世界的全部文字編碼能夠到 Unicode Consortium 和 codepoints 中查看,對應的實現有UTF八、UTF16
、UTF-32。es6
UTF八、UTF1六、UTF-32最大區別在於對應多少字節的數據,越大能存儲的字符也就越多。其中 UTF-8 就是在互聯網上使用最廣的一種 Unicode 的實現方式,也就是如今 html 中最常看到的 <meta charset="UTF-8">
所聲明字符集。github
UTF 最大的特色在於可變長的字節
,例如 UTF8 能夠是 1到4個字節來記錄 萬國碼
,爲何這麼設計呢?平常使用獲得的字符對應的字符編碼沒有必要佔用這麼多字節,例如 0000 0000
和 0000 0000 0000 0000
都能表示 0,那使用更短的字節所佔用的空間更小,傳輸的速度更快。數據庫
在統一編碼的過程當中還出現了一個字符集 UCS-2
,它固定使用 2個字節來編碼 與 UTF 可變長度字符編碼有必定程度上的不一樣,可是隨着統一進程下被 UTF-16
收編了。後端
瞭解字符基本原理和進程後,那麼 JavaScript 是什麼編碼呢?沒錯它就剛纔 小插曲
中提到的 UCS-2
,緣由是 JavaScript 誕生時 UTF-16
尚未出現。數組
可是如今你們都在使用 UTF 可變長度字符編碼
,UTF-16
的可變字節爲 2個或者 4個,而 UCS-2
卻只有 2個。這樣兩個字符集之間就有存在一個 UCS-2
沒法識別的 4字節,JavaScript 在處理字符時會傻傻
的按着 UCS-2
的兩字節去處理,再加上字典
裏沒有這個字符笨笨
的小腦殼瓜沒法處理只能輸出亂碼。
因爲 emoji 表情的普及,並且 emoji 恰好就是處於 UCS-2
的字典
以外,在前端開發中遇到可能出現 emoji 的地方須要當心謹慎:
如今最爲經常使用的 emoji 表情爲 4個字節編碼表示,因爲 UCS-2
固定兩個字節,在統計長度時 emoji 會被當作兩個 UCS-2
字符,結果會和咱們預期的輸出大了一倍。
let emoji = "😊"; // 輸出 2 console.log(emoji.length);
利用 es6 的 Array.prototype.from
和 spread
來作字符串轉數組並計算長度:
let emoji = "😊"; // 輸出 1 console.log(Array.from(emoji).length); // 輸出 1 console.log([...emoji].length);
若是不支持 Array.prototype.from
能夠利用正則替換把 4字節的字符替換爲 _ 並計算長度:
let emoji = "😊"; function countSymbols(string) { var regexAstralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; return string .replace(regexAstralSymbols, '_') .length; } // 輸出 1 console.log(countSymbols(emoji));
如同上面所講 emoji 會被當作兩個 UCS-2
字符,反轉的時候 4個完整的字節會被硬生生的拆分開來,可使用 Esrever 來解決。
let emoji = "😊"; // 輸出爲兩個亂碼字符 console.log(emoji.split('').reverse().join(''));
在使用 String.prototype.charCodeAt
和 String.fromCharCode
會出現問題。可使用 ES6 的兩個新方法來替換 String.prototype.codePointAt
和 String.fromCodePoint
。
正則裏 .
表示匹配一個字符,可是 UTF-16
4字節字符會被當作兩個字符來處理,進而引發錯誤。ES6 給出了新的解決方法加上 u
標誌 /./u.text('😊')
,因此寫正則的時候要記得加上哦。
對於字符串的遍歷可使用 for...of
語句。
若是後端數據庫運行存儲 emoji 做爲用戶名時,前端在限制用戶名長度判斷時須要注意UTF-16
4字節字符帶來的統計錯誤,其餘相似場景同理可得。
小提示:在作微信公衆號開發時,因爲用戶名和用戶輸入可能出現 emoji 等字符,須要對數據庫進行字符集的設置。不要問我爲何知道,由於個人眼裏常含淚水。
在困惑的城市裏總少不了並肩同行的
夥伴
讓咱們一塊兒成長。
點贊
。小星星
。m353839115
。
本文原稿來自 PushMeTop