每日 30 秒 ⏱ 字符編碼排雷錄

簡介

字符編碼、字符長度錯誤、截取字符錯誤、UTF八、Unicodejavascript

計算機重重底層之下都是由 0 和 1 組合,可是你知道他們是怎麼一步步變成字符串的嘛?在咱們現實生活中最多見的例子能夠經過 wo 在新華字典中找到 這個字。一樣計算機經過 0 和 1 組合在 字典 中查找到對應的字符,那 字典 內容是什麼呢?html

起源

計算機誕生於 美國 它的使用者大多數使用英文,美國國家標準學會 便制定了這本字典包括了 26個大寫英文字母26個小寫英文字母10個阿拉伯數字等總共 256 個字符的 ASCII 字符集。前端

混亂

ASCII 用二進制來表示就是 0000 00001111 1111 被用得滿滿當當,漢字就沒有地方能夠放得下了這下怎麼辦?正所謂江山大有人才出,國標編碼 GB 系列出現了,其中最耳熟能詳的就是 GB2312java

那麼問題來了世界擁有 25003500 種語言,有文字的語言有 930 種。你能想象你在瀏覽不一樣語言界面的時候,須要本身不斷的去切換 字典 而且 每次切換查找不到的字符就會亂碼出現。git

統一

書同文,車同軌,行同倫。es6

上面這句話歌頌了秦始王具備跨時代意義的成就,可是現實世界中統一語言顯得不可能。那咱們可否換個思路解決這個問題呢?先思考一個問題:「把大象放入冰箱須要幾步」,答案你們都知道「打開,裝進去,關上」。那統一字符怎麼辦呢?那就是建立一個足夠大的字典把全部的字符都放進去。github

萬國碼

Unicode 萬國碼 轟隆一聲誕生了,顧名思義統一了全世界的全部文字編碼能夠到 Unicode Consortiumcodepoints 中查看,對應的實現有UTF八、UTF16 、UTF-32。數據庫

可變長度字符編碼

UTF八、UTF1六、UTF-32最大區別在於對應多少字節的數據,越大能存儲的字符也就越多。其中 UTF-8 就是在互聯網上使用最廣的一種 Unicode 的實現方式,也就是如今 html 中最常看到的 <meta charset="UTF-8"> 所聲明字符集。後端

UTF 最大的特色在於可變長的字節,例如 UTF8 能夠是 1到4個字節來記錄 萬國碼,爲何這麼設計呢?平常使用獲得的字符對應的字符編碼沒有必要佔用這麼多字節,例如 0000 00000000 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.fromspread 來作字符串轉數組並計算長度:

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.charCodeAtString.fromCharCode 會出現問題。可使用 ES6 的兩個新方法來替換 String.prototype.codePointAtString.fromCodePoint

正則匹配

正則裏 . 表示匹配一個字符,可是 UTF-16 4字節字符會被當作兩個字符來處理,進而引發錯誤。ES6 給出了新的解決方法加上 u 標誌 /./u.test('😊'),因此寫正則的時候要記得加上哦。

字符串遍歷

對於字符串的遍歷可使用 for...of 語句。

場景

若是後端數據庫運行存儲 emoji 做爲用戶名時,前端在限制用戶名長度判斷時須要注意UTF-16 4字節字符帶來的統計錯誤,其餘相似場景同理可得。

小提示:在作微信公衆號開發時,因爲用戶名和用戶輸入可能出現 emoji 等字符,須要對數據庫進行字符集的設置。

不要問我爲何知道,由於個人眼裏常含淚水。

一塊兒成長

在困惑的城市裏總少不了並肩同行的 夥伴 讓咱們一塊兒成長。

  • 若是您想讓更多人看到文章能夠點個 點贊
  • 若是您想激勵小二能夠到 Github 給個 小星星
  • 若是您想與小二更多交流添加微信 m353839115

微信公衆號

本文原稿來自 PushMeTop

相關文章
相關標籤/搜索