2005年3月16日

[PDA] Zaurus C760 新手筆記 (35) - UTF-8 and BOM

使用 Zaurus 的人大都知道,系統預設的編碼是 UTF-8。不過,Windows 上的 UTF-8 與 Zaurus 上的 UTF-8 雖然大致相同,但卻有小異。

可以參看 UTF-8 and Unicode FAQ for Unix/Linux 一文中的這一小節 What different encodings are there?,其中有一段:
In order to allow the automatic detection of the byte order, it has become customary on some platforms (notably Win32) to start every Unicode file with the character U+FEFF (ZERO WIDTH NO-BREAK SPACE), also known as the Byte-Order Mark (BOM). Its byte-swapped equivalent U+FFFE is not a valid Unicode character, therefore it helps to unambiguously distinguish the Bigendian and Littleendian variants of UTF-16 and UTF-32.
先說明一下什麼是 Byte-Order:在一些平台上,是把代表數值較大的 byte 放在前面,這稱為 Big Endian (BE) 的系統;有些平台則相反,是把代表數值較小的 byte 放在前面,稱為 Little Endian (LE) 的系統。像 Zaurus 是屬於 BE 的系統,而 wintel 架構則屬 LE。若同以 "中" 這個字來看,它的 Unicode 是 U+4E2D,故在 BE 上是以 0x4E 0x2D 來表示,而在 LE 上則以 0x2D 0x4E 來表示。

在某些平台上 (特別是 Win32 系統),文檔習慣以 U+FEFF 這個字元 (慣稱為 BOM: Byte-Order Mark) 開頭,來辦識該文檔是採 BE 或 LE 的方式編碼。若採 LE 方式編碼,BOM 會表示為 0xFF 0xFE,而在 Unicode 的定義中不存在 U+FFFE 這個字元,所以若前兩個 byte 為 0xFF 0xFE,則一定是 LE 編碼的文檔;反之,若採 BE 方式編碼,BOM 會表示為 0xFE 0xFF,而 U+FEFF 是在 Unicode 中的有效字元,故為 BE 編碼的文檔。又因為 U+FEFF 字元,在 Unicode 中代表的是一個不佔空間的 space 符號,所以即使沒被解釋為 BOM,也不會對閱覽者產生錯誤的訊息。

換句話說,若某文檔僅有一個 "中" 字。則採 BE 系統,且用了 BOM 字元時,該檔 Unicode 編碼的 binary 內容為:
0xFE 0xFF 0x4E 0x2D
採 LE 系統,且用了 BOM 字元時,該檔 Unicode 編碼的 binary 內容為 (wintel 架構下的典型情況):
0xFF 0xFE 0x2D 0x4E
但是在 Zaurus 上,習慣上並不用 BOM 字元,而是單純認定文檔採 BE 系統,所以該檔 Unicode 編碼的 binary 內容為:
0x4E 0x2D

但是,我們要談的是 UTF-8 編碼,它並不等同於 Unicode。若僅考慮 Unicode 從 U+0000 到 U+FFFF 的範圍,它是以下列的規則轉為 UTF-8 編碼:
U+0000 - U+007F:0xxxxxxx
U+0080 - U+07FF:110xxxxx 10xxxxxx
U+0800 - U+FFFF:1110xxxx 10xxxxxx 10xxxxxx
一樣以 "中" U+4E2D 為例,因為 0x4E 0x2D = 01001110 00101101,所以它的 UTF-8 編碼為
11100100 10111000 10101101 = 0xE4 0xB8 0xAD
同理可得 BOM 字元的 UTF-8 編碼為 0xEF 0xBB 0xBF。又,因為每個字元不見得剛好是 2 個 byte,所以不管是以 LE 或是 BE 為主的系統上,UTF-8 文檔大都採 BE 方式編碼。

換句話說,若某文檔僅有一個 "中" 字。當用了 BOM 字元時,該檔 UTF-8 編碼的 binary 內容為 (wintel 架構下的典型情況):
0xEF 0xBB 0xBF 0xE4 0xB8 0xAD
而在 Zaurus 上,因為不用 BOM 字元,所以該檔 UTF-8 編碼的 binary 內容為:
0xE4 0xB8 0xAD

前面所提看來頗為無用,但是在某些情況下,直接從 wintel 平台轉出的 UTF-8 文檔,會無法在 Zaurus 下使用

因 為 Unix 系統有所謂的 magic word,即是以檔案的前兩個 byte 來辨識該檔的性質。最簡單的例子就是 bash/perl/python 等 script 檔,開頭必定是 "#!"。若在 wintel 平台下轉出 UTF-8 文檔,則前兩個 byte 被 BOM 字元佔用,會導致 shell 無法判讀。

那該怎麼做比較能確保檔案不會被誤轉呢?我沒什麼好方法,不過只要是從 PC 上轉出的檔案,我必定先在 Zaurus 平台上以編輯器 (我是用 ZEditor) 重新開啟並儲存一次,以確保 BOM 字元不會在檔頭出現。

另外,雖然 Ultraedit 能夠讀懂 UTF-8 格式的文檔,而且在 menu 上也有把文檔轉為 UTF-8 的功能選項,但它實際上永遠是把 UTF-8 儲存成 Unicode 格式 (我在 Ultraedit v11 上測試多次,不管怎麼選擇,如何轉換,它實際上還是把檔案存成 Unicode 格式)。有興趣者可以下載安裝 WinHex, 直接看 ultraedit 輸出的 UTF-8 文檔,並與 Windows XP 的記事本軟體 (選擇另存新檔,即可選擇檔案格式) 輸出的 UTF-8 文檔比較。不過,因為 ZEditor 也可讀懂 Unicode 文檔,且在 Zaurus 上本來就要重新儲存一次,所以影響也不大。
註 1:Windows XP 的記事本,處理 Unicode 及 UTF-8 比 Ultraedit 更精準。例如,Big Endian 的 Unicode 文檔,Ultraedit 會無法辨識,但記事本卻能正確解讀。不過,記事本只認 DOS 的換行模式。
註 2:ZEditor (我用的是日文版) 會自行根據文本轉換內碼系統,在字少的狀況下容易誤判成 Shift JIS 編碼。例如,全文只有一個 "中" 字時。



1 則留言:

匿名 提到...

(我在 Ultraedit v11 上測試多次,不管怎麼選擇,如何轉換,它實際上還是把檔案存成 Unicode 格式)
----
我想可能是Ultraedit預設會自動轉成Unicode 的關係,可在
[Advanced] => [Configuration] => [General] => [Load/Save/Conversions]
取消 [Auto detect UTF-8 files] 這個選項試試看
:-)