RunToolz iconRunToolz
Welcome to RunToolz!
編碼Unicode開發者工具

字元編碼詳解:從ASCII到UTF-8

為什麼你的文字有時會變成問號和亂碼。一份關於字元編碼的實用指南。

RunToolz Team2026年1月16日6 min read

你開啟一個檔案,看到的是ü而不是u。或者資料庫在應該顯示名字的地方回傳????。又或者收到一封郵件,主旨裡散布著=?UTF-8?B?

歡迎來到字元編碼問題的精彩世界。

簡短的歷史

電腦儲存的是數字,不是字母。所以必須有人決定哪個數字代表哪個字母。1960年代,ASCII將0-127的編號分配給英文字母、數字和基本符號。字母「A」是65。空格是32。很簡單。

但ASCII只涵蓋128個字元。對英文夠用。對德語變音符號、日語漢字、阿拉伯文字,或者人類實際使用的成千上萬其他字元,就不夠了。

Unicode之前的混亂

不同地區發明了各自的編碼。西歐有ISO-8859-1。日本有Shift-JIS。俄羅斯有KOI8-R。中國有GB2312。每種在自己的生態系統內都運作良好。一旦混用,一切都崩潰了。

用一種編碼儲存、用另一種編碼開啟的檔案會產生亂碼——那種你可能見過的錯誤字元大雜燴。UTF-8檔案被當作ISO-8859-1讀取時,cafe會變成café

Unicode解決了對映問題

Unicode給每個字元一個唯一的編號(稱為「碼位」)。拉丁字母A是U+0041。雪人是U+2603。每個表情符號、每種文字系統、每個數學符號都有自己的碼位。超過15萬個字元,還在持續增長。

但Unicode只是對映。它不說明如何將這些數字儲存為位元組。那是編碼的工作。

想親自試試嗎?統計字元和位元組數

UTF-8:勝出的編碼

UTF-8是網際網路大部分儲存Unicode文字的方式。關鍵技巧:每個字元使用可變數量的位元組。

  • ASCII字元(英文字母、數字):每個1位元組
  • 歐洲帶重音字元:每個2位元組
  • 亞洲字元(CJK):每個3位元組
  • 表情符號和稀有符號:每個4位元組

這意味著UTF-8中的英文文字與ASCII完全相同。舊系統不會出問題。但你仍然可以表示世界上任何字元。

目前超過98%的網站使用UTF-8。編碼之戰已經結束,UTF-8獲勝了。

UTF-8 vs UTF-16 vs UTF-32

UTF-8: 可變寬度(1-4位元組)。對英文為主的文字高效。Web標準。

UTF-16: 可變寬度(2或4位元組)。JavaScript、Java和Windows內部使用。每個字元至少2位元組,所以對ASCII文字效率較低。

UTF-32: 固定寬度(每字元4位元組)。簡單但浪費。很少用於儲存或傳輸。

JavaScript的string.length計算的是UTF-16程式碼單元,不是字元。所以"😀".length回傳2,不是1。

當編碼出問題時

用錯誤的編碼讀取檔案。 位元組本身沒問題,但被錯誤地解釋了。解決方案:開啟時指定正確的編碼。

資料庫字元集不匹配。 應用程式傳送UTF-8,但資料庫欄位設定為latin1。ASCII範圍外的字元被損壞。解決方案:將資料庫設為utf8mb4(MySQL中不是utf8,那個只處理3位元組字元)。

HTTP缺少charset標頭。 如果伺服器不傳送Content-Type: text/html; charset=utf-8,瀏覽器只能猜。有時猜錯。

想親自試試嗎?URL編碼/解碼

實用建議

始終使用UTF-8。 除非有非常具體的理由不用,UTF-8是所有場景的正確選擇。

明確宣告編碼。 HTML中:<meta charset="utf-8">。HTTP中:Content-Type: text/html; charset=utf-8。不要讓系統去猜。

注意字串長度。 在JavaScript中,字元計數遇到表情符號和組合字元時會變得複雜。使用Array.from(str).lengthIntl.Segmenter API來取得準確計數。

注意BOM。 位元組順序標記(U+FEFF)有時出現在UTF-8檔案開頭。它不可見但可能導致解析器出錯。某些編輯器會默默添加它。


字元編碼不是什麼令人興奮的話題,但理解它能省下數小時的除錯時間。到處使用UTF-8,明確宣告它,當你看到亂碼時,就會準確知道該去哪裡找原因。