UTF-8 與 BOM

Unicode 簡介

Unicode(中文翻成「萬國碼」或「統一碼」)是一種文字編碼方式。主要的目的,是希望能提供一套統一的標準,以數字的方式表示全世界人類使用的文字,方便利用電腦、網路儲存及傳輸文字資訊。詳細的歷史沿革,網路上可以找到很多文章,在此不再贅述。

關於碼點 Code Point

每一個被 Unicode 規範定義的文字,都有一個獨一無二的數值,我們稱之為「碼點 (Code Point,亦翻作「代碼點」)」。通常我們會用 U+xxxxxxxx 代表 16 進位的數字)這樣的格式來表示碼點。

舉例來說,中文的「我」這個字的碼點是 U+6211,以 16 進位來表示的話是 0x6211,換算成 10 進位就是 25105。下面這段簡單的 HTML 文件:

  <BODY>
    <H1>&#25105</H1>
    <H2>&#x6211</H2>
  </BODY>

會顯示大小不同的兩個「我」字。

編碼方式

雖然每個文字都有一個碼點,但實際應用上,為了電腦儲存、處理、傳輸方便,人們發展出了各種不同的編碼方式。通常我們稱之為 Unicode Transformation Format,縮寫為 UTF,中文譯為「Unicode 轉換格式」。常見的編碼方式有 UTF-7、UTF-8、UTF-16 以及 UTF-32 等。本文主要討論的為 UTF-8 與 UTF-16。

UTF-16

UTF-16 是 Windows 和 Mac OS X 預設的 Unicode 編碼方式。名稱中 "16" 的意義是「以 16 位元 (bit) 的數字為單位,去表示 Unicode 的文字」。碼點在 U+FFFF 以下的字,其 UTF-16 編碼基本上就跟碼點一樣。例如「我」的 UTF-16 編碼就是 0x6211

UTF-8

UTF-16 有一個缺點:在 Unicode 發明以前,大部份的文字檔案都是以 ASCII 格式儲存的;但 UTF-16 和 ASCII 並不相容。舉例來說,ABC 這個字串以 ASCII 表示的話,就是 0x41 0x42 0x43,但 UTF-16 則為 0x00 0x41 0x00 0x42 0x00 0x43。這個問題會導致現有處理文字的程式、函式庫全部得要重寫,現有的文字檔案也全部得要轉換成 UTF-16 才能使用。

為了解決這個問題,有人發明了 UTF-8 這種格式。原本 ASCII 定義的範圍(碼點 U+0000 ~ U+007F)的文字,UTF-8 和 ASCII 完全相容。但有一好沒兩好:碼點稍微大一點,所需要的儲存空間就會成長得很快。例如「我」用 UTF-16 編碼的話只需要 2 個位元組 (byte) 的空間;但用 UTF-8 則需要 3 個位元組 (0xE6 0x88 0x91)。至於碼點如何轉換成 UTF-8 的編碼,請自行 google。

Linux 預設的 Unicode 編碼方式就是 UTF-8。此外,目前世界上絕大部份的網頁也是以 UTF-8 方式儲存/傳輸的。

在此整理一下幾個文字的碼點與編碼比較表:

文字 碼點 UTF-16 UTF-8
A U+0041 0x0041 0x41
£ U+00A3 0x00A3 0xC2 0xA3
U+6211 0x6211 0xE6 0x88 0x91
𐌻 U+1033B 0xD800 0xDF3B 0xF0 0x90 0x8C 0xBB

位元順序與 BOM

UTF-16 還有一個問題。它的編碼基本單位為 16 位元的數字;但文字檔案儲存時大多是以位元組(長度為 8 位元)為單位。當我們想把 0x6211 這個字存進檔案裡時,究竟該存成 0x62 0x11 還是 0x11 0x62 呢?

唸資訊的人應該都知道,這是典型的 "Big-Endian vs Little-Endian" 的問題。而這兩種做法其實並無優劣之別,因此兩種方法都符合 UTF-16 的規範。

但問題還是得解決。讀檔案的人(或程式)要怎麼知道這個檔案是 Big-Endian 還是 Little-Endian 呢?Unicode 的做法是:所有 UTF-16 的檔案開頭,都會插入 U+FEFF 這個「文字」。讀取檔案的時候,藉由觀察開頭 2 個位元的順序(0xFE 0xFF 或是 0xFF 0xFE),就能知道這個檔案使用的順序為何。

U+FEFF 這個「文字」被稱做 Byte Order Mark,縮寫為 BOM,中譯為「位元順序記號」。

UTF-8 需要 BOM 嗎?

相較於 UTF-16,UTF-8 的編碼基本單位為 8 位元,也就是 1 個位元組,因此沒有 endian 的問題。雖然 BOM 能以 UTF-8 表示(畢竟它是有碼點的合法文字),但存在的意義不大,Unicode 的規範中甚至明文寫著:「UTF-8 不需要且不建議使用 BOM。」因為它會破壞 UTF-8 與 ASCII 的相容性。

BUT!!

微軟說:「(UTF-8) 要有 BOM!」就有了 BOM….(笑)

微軟認為,UTF-8 的 BOM 有助於程式判斷該檔案是否以 UTF-8 編碼;而一般微軟提供的文字編輯器,例如記事本 (Notepad),在「另存新檔」時若編碼選擇 UTF-8,檔案的前面就會自動被加上 BOM。

然而,許多程式在處理 UTF-8 編碼的檔案時,都沒有特別處理 BOM,結果就是把 BOM 當成是多餘的字元,導致程式發生錯誤。

如何移除 UTF-8 的 BOM?

如果只有少數的幾個檔案要處理,可以利用文字編輯器手動處理。例如免費的 Notepad++,就可以利用選單中的「編碼 -> 轉換至 UTF-8 碼格式(檔首無 BOM)」功能,將檔案前面的 BOM 拿掉。

如果想要大量批次轉換檔案,建議可以利用批次檔(或 shell script)搭配簡單的 awk 程式處理。詳細的 awk 程式可以參考文章後的連結。


參考資料

  1. Wikipedia: Unicode

  2. Wikipedia: UTF-8

  3. Usage of character encodings for websites

  4. UTF-8 Everywhere

  5. StackOverflow: How to make Notepad to save text in UTF-8 without BOM?

  6. StackOverflow: Using awk to remove the Byte-order mark

  7. 約耳談軟體:每個軟體開發者都絕對一定要會的Unicode及字元集必備知識 (沒有藉口!)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *