【Day 19】從 ASCII 到 Unicode——文字編碼全面升級

從 ASCII 到 Unicode——文字編碼全面升級

從 ASCII 到 Unicode——文字編碼全面升級

有一次在網咖查資料時,我打開某個網頁,結果裡面的文字全都是亂碼,看起來就像這樣……

亂碼

我已經忘了當時是在查什麼,只記得那時候用 Google 找資料,點進去某個網站後整頁全是亂碼。

「ㄟ,這網站怎麼變這樣啊?」我忍不住問了在網咖認識的朋友。

他笑了一下,說:「很簡單啦,打開瀏覽器設定,把字元編碼改成 UTF-8 就好了。」

Note
早期可以透過瀏覽器手動設定字元編碼,但現在大部分的瀏覽器都會自動偵測並設定,因此亂碼的情況已經很少見了。

「哎!變成正常了!為什麼啊?」我問。

「阿災~反正遇到亂碼就改成 UTF-8 就對了。」他說。

當下我想說問題解決就好,所以也沒多問,哪知道隔天資訊課就教到當下我只覺得問題能解決就好,也沒多想,哪知道隔天的資訊課,老師剛好就在講「文字編碼」。

文字編碼的基礎

還記得前一篇提到的 ASCII 嗎?我們說過 ASCII 是一種「文字編碼」,它透過二進位對應到字母、數字和符號,讓這些內容能顯示在電腦畫面上。

不過,ASCII 早期僅支援 7 位元,也就是 2^7 = 128 個字元(代表 128 種不同符號)。這樣的限制,使得 ASCII 無法涵蓋其他語言的字元,例如中文、日文、韓文等等——畢竟它在設計之初,就是專門針對英語而來。

Note
也因為早期 ASCII 是針對英語設計的,所以後來才會改叫「US-ASCII」,意指「美國標準資訊交換碼」。

為了彌補 ASCII 的不足,後來又延伸出了「EASCII(Extended ASCII)」。那麼,EASCII 和 ASCII 有什麼差別呢?最主要的不同點是:EASCII 擴充到 8 位元,也就是 2^8 = 256 個字元,因此能支援更多符號。

由於 EASCII 是在 ASCII 的基礎上擴充而來,所以它也具備以下特性:

  • 向下相容:EASCII 的 0~127 字元與 ASCII 完全相同,這意味著所有的 ASCII 字元在 EASCII 中仍然可以使用。
  • 擴充字元集:在原本 ASCII 的基礎上,EASCII 新增了 128~255 的字元範圍,用來表示更多符號,例如歐洲語言的特殊字母、數學符號等等。

不過這裡要特別注意,EASCII 並不是一個國際統一標準,因為不同的系統或應用程式可能會使用不同的 EASCII 編碼方式,當時可說是「各自為政」,也因此常常引發「亂碼」問題。

以下提供一個 ASCII 與 EASCII 表格給參考:

項目 ASCII EASCII(Extended ASCII)
全名 American Standard Code for Information Interchange Extended ASCII
位元數(bit) 7 bits → 128 組合 8 bits → 256 組合
數值範圍 0 ~ 127 0 ~ 255
是否標準定義 是,國際標準 各家自定擴充 → 沒有「全球一致標準」
高位元區(128~255) 無定義 各家放特殊符號、國際字元、圖形字元
主要用途 基本英文、數字、標點、控制碼 支援歐洲語言、圖形符號、商用需求等

那為什麼現在我們已經不用再擔心編碼問題了呢?
是因為瀏覽器夠聰明能自己辨識?還是因為電腦硬體升級了?

其實都不是,真正的原因是後來出現了「UNICODE(萬國碼)」這個標準。

UNICODE(萬國碼)

這裡先破個迷思:很多人(包含以前的我)都以為 Unicode 是一種字元編碼方式。

前面介紹過的 ASCII、EASCII 確實屬於字元編碼方式,但 Unicode 實際上並不是編碼方式,而是一個「字元集」。

你可以把它想像成一本「國際字典」,收錄了世界上所有文字和符號,並且為每個字元分配一個獨特的編碼,這個編碼就叫做「碼點(Code Point)」。
例如:

字元 碼點(Code Point)
A U+0041
B U+0042
C U+0043
U+4F60
U+6211
U+4E2D
😀 U+1F600
🐱 U+1F431

透過這樣的設計,Unicode 能夠支援全世界的文字與符號,並有效避免亂碼問題。而從上面的表格也能看出,Unicode 的碼點都是以 U+ 開頭的十六進位數字來表示。

那麼 UNICODE 所採用的位元是多少呢?

是 21 位元,這也代表 UNICODE 比 ASCII 和 EASCII 都要大得多,因為:

  • ASCII 只有 7 位元,能表示 2^7 = 128 個碼點。
  • EASCII 擴充到 8 位元,能表示 2^8 = 256 個碼點。
  • UNICODE 擴充到 21 位元,能表示 2^21 = 2,097,152 個碼點。

這樣的位元數已經足夠支援世界上所有的文字、符號等。

在前面我們說 UNCODE 是一個「字元集」,那麼它的編碼方式又是什麼呢?其實 UNICODE 有三種編碼方式:

  1. UTF-8:又稱可變長度字元編碼,使用 1 到 4 個位元組來表示一個字元,向下相容 ASCII,適合用於網路傳輸。
  2. UTF-16:使用 2 或 4 個位元組來表示一個字元,適合用於內存和檔案存儲。
  3. UTF-32:使用 4 個位元組來表示一個字元,簡單但佔用空間較大。

以我們最常見使用的是 UTF-8 為例,UTF-8 的編碼方式是可變長度的,什麼是可變長度呢?

簡單來講,每個字元所佔用的位元組會隨著字元的複雜度而有所不同

  • 對於 ASCII 字元(0~127),剛好對應 U+0000 到 U+007F,使用 1 個位元組表示。
  • 對於 U+0080 ~ U+07FF 的字元,使用 2 個位元組表示(這是屬於歐系語言的字元)。
  • 對於 U+0800 ~ U+FFFF 的字元,使用 3 個位元組表示(這是屬於中日韓、數學符號的區間)。
  • 對於 U+10000 ~ U+10FFFF 的字元,使用 4 個位元組表示(這是屬於表情符號、特殊符號或是生僻字元的區間)。

這也是為什麼前面表格你會看到 A 的編碼是 U+0041,而 😀 的編碼是 U+1F600 的原因。

最後,我們也來聊聊 UTF-8 常用的進位方式-「十六進位(Hexadecimal)」。
簡單來說,十六進位就是十進位的延伸。我們知道十進位用 09 表示數字,超過 9 就進位。
而在十六進位中,除了 0
9 之外,還加入了 AF 這六個字母來表示 1015。

所以表示方式如下:

1
0 1 2 3 4 5 6 7 8 9 A B C D E F

關於 UTF-8,你只要先掌握幾個重點就好,其他細節有需要再去查就行:

  • UTF-8 是一種可變長度的編碼方式,隨著字元的複雜度而有所不同。
  • UTF-8 向下相容 ASCII,也就是說,所有的 ASCII 字元在 UTF-8 中仍然可以使用。
  • 十六進位是 UTF-8 的表示方式,這樣可以更方便地表示字元的編碼。
  • UNICODE 是一個字元集,它收錄了世界上所有的文字、符號等,並且不會有亂碼的問題。

以上就是關於 UNICODE 的基礎介紹,這樣的編碼方式讓我們可以在網路上自由地使用各種文字、符號,而不必擔心亂碼的問題囉~

當然,你也可以寫程式來查詢 UNICODE 的碼點,這邊我就以 JavaScript 為例:

1
2
3
4
5
document.addEventListener('keydown', (event) => {
const key = event.key;
const codePoint = key.codePointAt(0).toString(16).toUpperCase().padStart(4, '0');
console.log(`Key: ${key}, Code Point: U+${codePoint}`);
});

See the Pen UNICODE UTF-8 by Ray (@hsiangfeng) on CodePen.

這段程式碼會在你按下鍵盤時,顯示「按下的鍵」以及對應的 Unicode 碼點:

結語

這裡先提醒一下讀者,因為接下來的章節會開始介紹一些程式碼與工程相關的知識,過程中可能會需要用到開發者環境,例如:

當然,你也可以選擇不安裝這些工具,單純閱讀內容也完全沒問題,只是會少了一些實作體驗。
如果你有興趣,還是建議把工具安裝起來,這樣會更有感覺。

另外也不用擔心,後面的章節並不會深入鑽研程式碼,而是以基本概念為主,輕鬆帶過就好~

同步更新

本文將同步更新至以下網站: