UTF-8的BOM說明

edited 十月 2013 in 進階PHP討論
網址: http://mfhsieh.blogspot.com/2005/03/pda-zaurus-c760-35-utf-8-and-bom.html
使用 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 編碼。例如,全文只有一個 "中" 字時。

原始討論: http://twpug.net/x/modules/newbb/viewtopic.php?topic_id=460

評論

  • edited 三月 2005
    我這裡一直都用EmEditor來編寫程式,存成UTF-8 w/o BOM,因為這樣對大多數環境與工具的適應性最高。
  • edited 四月 2005
    我看了一下資料,大致了解win32系統下的文檔習慣以特定字元 (慣稱為 BOM: Byte-Order Mark) 開頭,來表示該文檔是採 BE 或 LE 方式編碼.
    大概是APACHE在接收到perl文件請求時,假如httpd.conf中的定義是交由由shebang line明定之路徑下的perl解譯器處理,那麼apache就得去讀取perl文件的第一行,這時問題就出現了,因為apache看不懂BOM這一特殊用途字元,且剛好BOM又位於文件第一行之開端,所以才會導致client browser接收到"伺服器無法處理的內部問題等錯誤訊息..."

    Q1:
    只是我還是不了解,ANSI編碼儲存網頁文件時不也有BOM的情形嗎?
    為何用ANSI就沒事,UTF-8就有事??

    Q2:
    為何APACHE會看不懂BOM字元呢?
    (難道它不支援UTF-8嗎?)

    Q3:
    順便請教,
    一般編寫網頁檔案需要注重編碼的選擇嗎?
    若想讓文件更有可攜性,例如移轉於linux與windows之間,
    是否以utf-8編碼網頁相關文件較好呢?
  • edited 四月 2005
    A1: 因為過去標示分行的BOM已經使用很久,大部分的軟體都支援;而UNICODE是新的標準,許多軟體還無法立即的跟進、支援,因此會有問題。

    A2: APACHE好像不用看懂,因為它只是負責傳輸;看不懂的是PHP存取檔案的函數,經常發生就是PHP把BOM字元當作一般字串輸出,而造成header already sent之類的錯誤

    A3: 其實一般的編碼與UTF-8之間都可以輕易轉換,資料的儲存倒還不會是很大的問題;重點是如果你要設計軟體,你就必須顧慮這些問題。當然,使用UNICODE還有一個好處,就是同一份文件裡面可以包含各國語文,以及比較肥...
  • edited 四月 2005
    :-) 我大概有頭緒知道怎麼問了,由於perl在5.6版以後就已支援utf-8,所以其compiler是ok的.

    Q1:
    問題只在apache這個本以unix為主的套件看不懂win32系統特有的BOM,所以才會有無法解讀shebang line的問題,當然也就找不到perl compiler來處理client請求的perl文件.但若以此認知,那麼改成ANSI編碼存檔時難道就無BOM的問題嗎?
    因為Win32系統習慣會對文檔加上BOM字元來區分文件屬於BE或LE...

    [補充]: 關於 Byte-Order Mark,這是我找到的解釋:
    先說明一下什麼是 Byte-Order:在一些平台上,是把代表數值較大的 byte 放在前面,這稱為 Big Endian (BE) 的系統;有些平台則相反,是把代表數值較小的 byte 放在前面,稱為 Little Endian (LE) 的系統。

    若採 LE 方式編碼,BOM 會表示為 0xFF 0xFE,而在 Unicode 的定義中是不存在 U+FFFE 這個字元的.

    若採 BE 方式編碼,BOM 會表示為 0xFE 0xFF,而 U+FEFF 剛好是在 Unicode 中的有效字元,代表的是一個不佔空間的 space 符號,所以即使沒被解釋為 BOM,也不會對閱覽者產生錯誤的訊息.

    Q2:
    因此,會不會是出在utf-8本身,
    由於它沒有BOM的支援,所以當我存檔時,windows的記事本程式會為文件標上 U+FEFF 字元.
    而當apache收到perl的文件請求進而去讀取shebang line時(這點我不確定正確與否,因為在其他討論區看過有人說什麼apache不會去讀文件只會傳送文件...的論調,so,若您清楚的話還請您為我釐清,在此我先假設apache會讀取請求的文件),所以假設文件是用ANSI編碼,那麼BOM會被正確解讀,
    但若文件是以UTF-8存檔,即便apache有支援utf-8,但若讀到BOM時,它不是當成space就是認為無意義...而不管何者發生,就如前所說,因為在 Unix 系統有所謂的 magic word,即以檔案的前兩個 byte 來辨識該檔的性質.例如 perl script 檔的shebang line,開頭必定是 "#!".因此原就是以 unix 為主的 apache 也就無法判讀....因此client 端的 browser 才會收到"伺服器的內部錯誤..."訊息.


    Q3:
    關於:
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    我當然有寫上,也正因如此browser還會把中文字顯示成亂碼我才搞不懂??

    p.s 不好意思,再次說明我的情形:
    我將原先以ANSI存檔的網頁文件重新以記事本或者EmEditor存成utf-8,使用後者時我還選擇不加上BOM這一特殊用途字元,且也以CR+LF的windows預設換行的方式重新存檔.....當然我也有在browser工具鈕的"檢視"中的"編碼"選擇"自動選取".....god! 快來救救我啊!


    Q4:
    最後就是ANSI與BIG5有什麼不同?
    You know,關於
    <meta http-equiv="Content-Type" content="text/html; charset=big5">
    為何不能寫成
    <meta http-equiv="Content-Type" content="text/html; charset=ansi">
    以往編寫網頁文件時都是以ansi存檔,即表示其也支援"中","英"字元符號不是嗎?


    *******************************************************************
    為免於我的解讀有誤,以下附上我所查詢的BOM等資訊來源:
    UTF-8 and BOM
    :-? :-? :-?
  • edited 四月 2005
    A1. Win32系統專用的BOM只是標示分行符號的差異,至於UTF-8的BOM則是標示使用的編碼,兩者同樣都可能造成軟體讀取問題;而ANSI的ASCII行之有年,無法處理的軟體很少,標準的定義中也沒有BOM這個東西。

    A2. Perl我不熟,不過Apache應該是只有傳輸Perl處理完的資料,會出現錯誤訊息的原因大多是程式在處理過程中出現問題。

    A3. 可能是你的APACHE有設定預設編碼造成?

    A4. charset是要註冊才能使用的,可以看看目前註冊的列表:

    http://www.iana.org/assignments/character-sets

    其實ANSI應該是組織名稱,文字的標準是ASCII;BIG5的編碼就是拉兩個ASCII字元組合而成。

    我在字元編碼這個部分並不是很深入,基本上答案也僅供參考。
  • edited 四月 2005
    一般用windows的記事本存寫好的網頁文件時都會有個編碼選擇:
    utf-8
    unicode
    ansi...

    Q1:
    我想問的是,其 ANSI 指的是 BIG5 還是單純的 ASCII code呢?
    (由於其能儲存中文字元所以我會猜是BIG5...不知對不對?)

    我會這樣問是因為:
    一個是雙位元組,一個則是單位元組,
    前面前輩有提到 BOM 是WIN32為多位元組編碼系統設計的,既如此,
    當我的 perl script 以 ANSI 存檔時為何不會有 BOM 的問題呢?

    Q2:
    若Q1的答案是BIG5,
    那麼例如我由XOOPS,MT BLOG等網站抓下來的檔案,我想若非以UTF-8就是以ANSI(ASCII)為編碼的吧.
    既如此,為何當我以記事本開啟這些文件時都能正常顯示呢?
  • edited 四月 2005
    A1. BIG5使用原本的ASCII編碼的保留區域,所以即使儲存成BIG5文件,文件的格式還是ASCII(ANSI...原來記事本用這個)

    那個WIN32的BOM好像是手誤 :-D ,沒有這個東西,BOM是用來辨識UNICODE文件的

    A2. 記事本應該會自動判斷,我嘗試用記事本另存成UTF-8編碼,然後移除它的BOM,看來讀取還是正常的。至於細節...麻煩問微軟...
  • edited 四月 2005
    您好,再請教一下:

    關於PHP的編碼轉換函式iconv():

    我發現該函數的"字碼集網站"上(http://www.gnu.org/software/libiconv/)的chinese區塊中CP950與BIG5都有定義到,這樣是否表示此兩有什麼不同之處?

    會這樣問是因我的MYSQL預設使用BIG5來存資料,若我要將調出來的資料轉換成UTF-8時,
    以下兩寫法何者才是??
    $newstr = iconv( "CP950" , "UTF-8" , $oldstr);
    $newstr = iconv( "BIG5" , "UTF-8" , $oldstr);
  • edited 四月 2005
    CP950印象中是M$基於BIG5的延伸,所以一般只適用於M$系列的作業系統,並不清楚是否有其他環境在使用;有興趣可以看看他們的官方資料:

    http://www.microsoft.com/globaldev/reference/dbcs/950.htm

    而轉換的過程中,單純的中文應該用BIG5就可以,CP950好像還定義了一些延伸字吧,像是日文之類的。
  • edited 四月 2005
    :lol: Thanks a lot !

    不好意思,再請教有無可以檢查資料編碼種類的函式可用?


    還有之前從您得知的big5字串專用的改良版php字串函式還滿好用的,
    不知是否有utf-8版本的?


    還有關於MySQL如何支援UTF-8呢?
    (因我在安裝APPSERV v2.5.1時發現它雖支援相當多的編碼,但就是找不到有utf-8可選)
  • edited 四月 2005
    1. http://tw.php.net/manual/en/function.mb-detect-encoding.php

    2. 那些函式只是為了過渡時期存在,現在可以善用ICONV與MBSTRING系列函數

    3. 不明,我都是等你們當完白老鼠才開始玩新東西 :-D
  • edited 四月 2005
    請教關於mbstring.encoding_translation :
    # Beginning with PHP 4.3.3, if enctype for HTML form is set to multipart/form-data and mbstring.encoding_translation is set to On in php.ini the POST'ed variables and the names of uploaded files will be
    converted to the internal character encoding as well. However, the conversion isn't applied to the query keys.

    mbstring.encoding_translation = Off/on

    何謂:HTTP input character ?

    是指當php接收到各國語系user在browser 表單所輸入的data (method="post")時是否進行自動轉換嗎?

    internal character encoding , query keys 又是指什麼?


    另外請教apache的httpd.conf中有一行如下是作什麼的?
    AddType application/x-httpd-php-source .phps
  • edited 四月 2005
    1. 透過用戶端網頁瀏覽器輸入的字元

    2. 對,但是包含 enctype=multipart/form-data 這個條件

    3. internal character encoding = 伺服器系統使用的字元編碼

    query keys = 傳遞字元的索引值,例如 <input type='text' name='索引' value='都可'> ,傳遞後 '索引' 並不會自動轉換,而'都可'會轉換為系統預設字元編碼
  • edited 四月 2005
    抱歉忘了問 mbstring.encoding_translation 設定為on有何好處?
    (一般適合設成on的網站多是何種需求呢?)
  • edited 四月 2005
    這個是由日本人開發出來的,因為日文也有類似問題;使用這個函式集可以相容於許多早期的系統,降低系統移轉的困擾,剛好適合現在這個過渡時期。

    不過我並不清楚當初開發的真正原因。
Sign In or Register to comment.