字符編碼、字符長度錯誤、截取字符錯誤、UTF八、Unicodejavascript
計算機重重底層之下都是由 0 和 1 組合,可是你知道他們是怎麼一步步變成字符串的嘛?在咱們現實生活中最多見的例子能夠經過 wo
在新華字典中找到 我
這個字。一樣計算機經過 0 和 1 組合在 字典
中查找到對應的字符,那 字典
內容是什麼呢?html
計算機誕生於 美國
它的使用者大多數使用英文,美國國家標準學會
便制定了這本字典
包括了 26個大寫英文字母
、26個小寫英文字母
、10個阿拉伯數字
等總共 256
個字符的 ASCII 字符集。前端
ASCII 用二進制來表示就是 0000 0000
到 1111 1111
被用得滿滿當當,漢字就沒有地方能夠放得下了這下怎麼辦?正所謂江山大有人才出,國標編碼 GB
系列出現了,其中最耳熟能詳的就是 GB2312
。java
那麼問題來了世界擁有 2500
至 3500
種語言,有文字的語言有 930 種。你能想象你在瀏覽不一樣語言界面的時候,須要本身不斷的去切換 字典
而且 每次切換查找不到的字符就會亂碼
出現。git
書同文,車同軌,行同倫。es6
上面這句話歌頌了秦始王具備跨時代意義的成就,可是現實世界中統一語言顯得不可能。那咱們可否換個思路解決這個問題呢?先思考一個問題:「把大象放入冰箱須要幾步」,答案你們都知道「打開,裝進去,關上」。那統一字符怎麼辦呢?那就是建立一個足夠大的字典
把全部的字符都放進去。github
Unicode 萬國碼 轟隆一聲誕生了,顧名思義統一了全世界的全部文字編碼能夠到 Unicode Consortium 和 codepoints 中查看,對應的實現有UTF八、UTF16 、UTF-32。數據庫
UTF八、UTF1六、UTF-32最大區別在於對應多少字節的數據,越大能存儲的字符也就越多。其中 UTF-8 就是在互聯網上使用最廣的一種 Unicode 的實現方式,也就是如今 html 中最常看到的 <meta charset="UTF-8">
所聲明字符集。後端
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.test('😊')
,因此寫正則的時候要記得加上哦。
對於字符串的遍歷可使用 for...of
語句。
若是後端數據庫運行存儲 emoji 做爲用戶名時,前端在限制用戶名長度判斷時須要注意UTF-16
4字節字符帶來的統計錯誤,其餘相似場景同理可得。
小提示:在作微信公衆號開發時,因爲用戶名和用戶輸入可能出現 emoji 等字符,須要對數據庫進行字符集的設置。
不要問我爲何知道,由於個人眼裏常含淚水。
在困惑的城市裏總少不了並肩同行的
夥伴
讓咱們一塊兒成長。
點贊
。小星星
。m353839115
。本文原稿來自 PushMeTop