2025年8月22日 星期五

編碼方式的介紹

 




作者:
楊先民
  
精誠資訊/恆逸教育訓練中心資深講師


※網路引用請註明完整出處


在上課時常常聽到有人說,存在資料庫中的員工姓名,在網頁顯示時會出現問號,不知道到底是什麼原因,所以本期就來探討一下編碼方式的討論。

各種不同編碼方式

首先要先了解,資料庫存放資料,並不是直接存放中文字,或是英文字,而是存放所謂的「內碼」,然後作業系統要將字呈現出來時,會對自身的內碼表,然後顯示該呈現的字元出來。

舉例來說,假設中文字「楊」的內碼是 AA01,在資料庫會存放的是 AA01,而不是「楊」這個中文字,而楊這個字要在別的作業系統呈現時,會尋找 AA01 的內碼,然後對照相對的字,呈現在畫面上。

所以說,如果資料庫用的編碼方式是 A,但是使用者端應用程式的作業系統所使用的編碼方式是 B,在 B 中自然找不到 A 所存放的內碼,所以資料也就無法呈現了。而且並不是單純你的應用程式支援該編碼就代表字能夠正確的顯示,主要還是作業系統

像 Windows Server,自 2000 之後全面支援 Unicode(不過應該推到更早,NT4 就有),不過得看你有沒有安裝該國的 Unicode 字集,如果沒有裝的話,還是會在畫面上呈現問號或是亂碼,所以並不是單純一句:支援 Unicode 就代表所有亂碼問題都解決了。

那到底有多少編碼方式呢?介紹一下:

處理各語系的字,有許多種編碼方式,一般可分為 ANSI 及 Unicode 這兩種。而一般 Unicode 又可以再細分成 UTF-8UTF-16UTF-32。一般我們俗稱的 Unicode 指的是 UTF-16 而非 UTF-8,常看到的 UTF-8 是網頁編碼時常用的規格。

UTF-32 (UCS-4)

先來看這不常用的 UTF-32 吧,它又稱 UCS-4 編碼,它對每一個 Unicode 碼位使用 32 位元,不過它會何不常用呢?因為 UTF-32 對每個字元都使用4位元組,就空間而言相當的浪費而沒有效率,所以被提起的機會不高。

ANSI 

早期英語系國家所使用的編碼方式,用來處理英文字母、數字及常用符號,只會佔用一個位元組,因為 1 個位元組有 8 個 bits,排列組合從 00000000 到 11111111,已經能收錄全部英文字母、數字以及常用符號,所以只需要 1 個位元組,相當節省。


然而,若是要處理中文字,使用 ANSI 是絕對不夠的,所以當年由中華民國財團法人資訊工業策進會與國內 13 家廠商合作進行「五大軟體專案」這五大軟體分別是「文書處理」、「資料庫」、「試算表」、「通訊」以及「繪圖」,稱為五大碼或大五碼,以繁體中文字來說,可以收錄的繁體中文字大概有一萬三千多個字,所以每個繁體中文字就需要二個位元組來儲存,不同國別的字可能佔用位元組可能會更多,而這種每個字為可變的位元組方法,稱作「多位元組(Multiple Byte)」的編碼,當年 SQL Server 6.5 就是所謂的「多位元組」的版本,可以用來處理中文資料,故多位元組的編碼不利程式讀取,因為英文字依然是使用 1 位元組,中文字使用多位元組,中英文混在一起常造成切割字元的問題,所以 SQL Server 6.5 當年我在使用時,有個重大的缺點,就是預存程序儲存,如果全用英文編碼,連註解都是英文的話,則程式不會有問題,但若是註解有中文的話,則有時預存程序開啟,程式只剩下一半的怪異情況,當然這些問題在 SQL Server 2000 之後就全部解決。


剛才提到, ANSI 不能完整解決所有的各種語系的國別字(萬國碼),若 ANSI 的檔案儲存 Big5 編碼方式, 就不能以 ANSI GBK(簡體中文)的方式開啟,因為 ANSI 的編碼,某個繁體中文字的二位元組內碼,也許是對印到簡體中文字的另一個字,甚至簡體中文字沒有對印到該二位組內容,所以用 BIG5 ANSI 來儲存,就不能用 GBK ANSI 來讀取,不然會造成亂碼, 不過英、數字母及常用符號,因為用同一個位元組內容儲存,就可以在不同 ANSI 編碼正確讀取。

UTF-16 (Unicode)

為了處理多國語言所採用的方式,號稱所有國家的字都可以用此編碼來相容, 每個英文字、數字或其它國家的字統一用二個位元組,以中文字「楊」為例,它在 UTF-16 中佔兩個二位元組,用 C 語言的來表示「楊」的十六位元表示寫法為 0x4A 及 0x69,其中 0x4A 為低位組, 0x69 為高位組,在 Windows 系統上則可能寫成 4A69,換句話說,就是由低位元組先寫,再寫入高位元組,而在 MAC 系統上則可能寫成 694A,也就是由高位元組先寫,再寫入低位元組, 假設有一份文字檔案是先寫低位元組再寫高位元組,稱作「UTF-16 LE(Little-Endian)」, 若是先寫高位組再寫低位組,稱作「UTF-16 BE(Big-Endian)」, 而為了分辨文字檔案中是何種 UTF-16,會在一份檔案的開頭一定會先寫 BOM(Byte Order Mark), 若開頭寫入 0xFF 再寫 0xFE 則為 UTF-16 LE 檔 案格式, 若開頭寫入 0xFE 再寫 0xFF 則為 UTF-16 BE 檔案格式。


SQL Server 中, XML 的資料型別是支援 UTF-16。



UTF-8

前面提到的 UTF-16 是「標準.Unicode」,因為 UTF-16 不管在處理中文字,英文或是不同國家的字時,每個字都是佔兩個位元組, 而對於英語語系國家而言,只會使用到英文、數字及一些特殊符號時,本來用 ANSI 編碼只佔用一個位元組來處理, 但是若是存成 UTF-16 的編碼時,就會佔用二個位元組反而佔用空間,所以 UTF-16 不利資料傳輸及太佔用系統儲存空間,為此所提出的一種解決方案,就是 UTF-8,採用 UTF-8 編碼時,會根據每個字它所對應的 Unicode 的範圍來決定每個字所佔用的位元組, 而每個字轉為 UTF-8 時,其所佔用的位元組可能為 1 ~ 6 位元組中的其中一種組合, 但是英、數及常用符號的話,每個字一定只會佔用一個位元組,其它國家的字就不一定如此,所以 UTF-8 對於英語系國家而言它的檔案會較小,但使用其它國別的字時,可能會佔用二個位元組或是比二個位元組還大空間,若檔案中的字太多不是英數等字,反而更佔空間,且因為每個字佔用空間可以 1 ~ 6 位元組中的其中一種組合,反而不利程式使用,故程式在讀取 UTF-8 格式時,一般情況會先將會轉成 UTF-16(Unicode),使每個字成為固定雙位元組,方便處理。


UTF-8 本身不需要 BOM辨識,可以在讀取整個檔案就可以判斷它是否為 UTF-8,而有另一種有 BOM 的 UTF-8 的存在,所以即便是 UTF-8,也有分這麼多種類的 UTF-8,而 SQL Server 嚴格來說並不是支援 UTF-8,網頁呈現多半是使用 UTF-8,換句話說,如果你想把 UTF-8 的網頁資料,轉存到 SQL Server 中的 Unicode 系統,必須要做一個轉換,幸好這個轉換會自動的進行,不需要寫程式的人操心,只需要設為「Unicode 即可」。


不過我們就另一個角度來看,應該程式端的環境,一定支援 Unicode 嗎?若不支援的話該如何是好?微軟的 MSDN 也提供了解決方案給我們:



結語

這裡可以肯定的地方是,SQL Server所安裝的Windows Server,一定是支援Unicode 的 ,所以不用管圖中的「伺服器非 Unicode」的情形,若你的用戶端也是支援 Unicode,則將會得到最好的效能,但若是用戶端是非Unicode 的話,則字碼要先進行轉換再存入資料庫中會比較好,不然會有問題。


有人說,SQL Server 支援的 Unicode 是所謂的 UTF-16,不過似乎微軟認為的是 UCS-2,,一般來說, UTF-16 是 UCS-2 的父集,若沒有 surrogate code points 的輔助,一般認為 UTF-16 等於 UCS-2,故在維基百科中寫,若有軟體宣稱自己支援 UCS-2,則代表所儲存的字元不會超過 2 個 bytes 的字集,這也就是為什麼 SQL Server 的 Unicode,每個字元長度不會超過 2 個 bytes 的原因。


這樣大家大概就了解,連 Windows 環境如果要 Unicode 自轉都有可能發生問題,更不用講異質資料庫之間的 Unicode 的轉換問題,最常提到的就是 Oracle 的字元轉到SQL Server的字元亂碼問題,你只要了解 Oracle 到底是支援什麼種類的 Unicode,轉換到SQL Server中就大概知道會不會有問題了。


0 意見:

張貼留言