簡介
? 更新平臺多偶爾會漏掉,如果覺得文章還行點個
star
防走失。
計算機重重底層之下都是由 0 和 1 組合,但是你知道他們是怎麼一步步變成字串的嘛?在我們現實生活中最常見的例子可以通過 wo
在新華字典中找到 我
這個字。同樣計算機通過 0 和 1 組合在 字典
中查詢到對應的字元,那 字典
內容是什麼呢?
起源
計算機誕生於 美國
它的使用者大多數使用英文,美國國家標準學會
便制定了這本字典
包括了 26個大寫英文字母
、26個小寫英文字母
、10個阿拉伯數字
等總共 256
個字元的 ASCII 字符集。
混亂
ASCII 用二進位制來表示就是 0000 0000
到 1111 1111
被用得滿滿當當,漢字就沒有地方可以放得下了這下怎麼辦?正所謂江山大有人才出,國標編碼 GB
系列出現了,其中最耳熟能詳的就是 GB2312
。
那麼問題來了世界擁有 2500
至 3500
種語言,有文字的語言有 930 種。你能想象你在瀏覽不同語言介面的時候,需要自己不斷的去切換 字典
並且 每次切換查詢不到的字元就會亂碼
出現。
統一
書同文,車同軌,行同倫。
上面這句話歌頌了秦始王具有跨時代意義的成就,但是現實世界中統一語言顯得不可能。那我們能否換個思路解決這個問題呢?先思考一個問題:“把大象放入冰箱需要幾步”,答案大家都知道“開啟,裝進去,關上”。那統一字元怎麼辦呢?那就是建立一個足夠大的字典
把所有的字元都放進去。
萬國碼
Unicode 萬國碼 轟隆一聲誕生了,顧名思義統一了全世界的所有文字編碼可以到 Unicode Consortium 和 codepoints 中檢視,對應的實現有UTF8、UTF16 、UTF-32。
可變長度字元編碼
UTF8、UTF16、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 字元處理
瞭解字元基本原理和程式後,那麼 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 的地方需要小心謹慎:
長度
BUG 預警
現在最為常用的 emoji 表情為 4個位元組編碼表示,由於 UCS-2
固定兩個位元組,在統計長度時 emoji 會被當做兩個 UCS-2
字元,結果會和我們預期的輸出大了一倍。
let emoji = "?";
// 輸出 2
console.log(emoji.length);
複製程式碼
BUG 解除
利用 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 等字元,需要對資料庫進行字符集的設定。
不要問我為什麼知道,因為我的眼裡常含淚水。
一起成長
如果您感覺有收穫可以點贊關注激勵我
,也歡迎到 Github 加個 star。
本文原稿來自 PushMeTop