天天看點

國際化使用UTF-8造成資料庫MSSQL Server 2000/2005存儲亂碼的分析

  看了許多網上使用MSSQL Server 2000/2005使用UTF-8造成資料庫存儲亂碼的描述,也說一下自己做的一個國際化項目的經驗。

這個項目描述:

架構:VC++的ATL Server進行開發;

頁面:web頁面是UTF-8編碼,CodePage=65001;

應用伺服器程式:編譯好的dll是Unicode編碼;

作業系統:中文Windows 2003 Server;

資料庫聯接方式:OLEDB

資料庫:中文MSSQL Sever2005,顯示Codepage=936,

字段都是支援Unicode的nchar,nvarcha,nText;

SELECT COLLATIONPROPERTY('Chinese_PRC_Stroke_CI_AI', 'CodePage')

936

________________________________________________

發現從Web頁面送出的資料到資料後查詢總是亂碼,經過檢查,發現儲存的資料直接就是是UTF-8編碼,其CodePage=65001,而資料預設顯示支援的Unicode語言版本Codepage=936(即是簡體中文),是以資料查詢的就是亂碼,想過兩種方案:

1,懷疑是資料時UTF-8編碼,而SQLServer是UCS-2 版本,相通過UTF-8-〉UCS-2轉換,發現還是行不通,仍然亂碼,此路不通;

2,搜尋了網絡發現并沒有現成的例子,經過測試中文系統使用Web頁面為GB2312編碼送出的資料到資料很正常,基于Windows2003是支援Unicode(UCS-2),排除作業系統的問題,這個過程經過分析可以這麼了解:936(Web頁面編碼GB2312)-〉Unicode(應用伺服器端)-〉Unicode(資料庫OLEDB傳輸)-〉UCS-2(資料庫)-〉自動轉化為936(Unicode簡體中文語言版本),而使用UTF-8的Web頁面這個過程就是:65001(Web頁面編碼UTF-8)-〉Unicode(應用伺服器端)-〉Unicode(資料庫OLEDB傳輸)-〉UCS-2(資料庫)-〉自動轉化為936(Unicode簡體中文語言版本),是以最後的使用資料分析查詢器看到的就是亂碼,最後确定的方案就是兩種手段:一是更改資料查詢分析器的Codepage為65001,很顯然目前的SQLServer沒有這個功能(沒找到?知道的告訴我);二是将前端的UTF-8轉為GB2312編碼,很顯然這個最後成功了,這個過程程式還是保留了所有的多語言的特性,一位UTF-8-〉GB2312,這個過程是無損的(新的驗證會出現部分方塊無法顯示),因為是UTF-8->Unicode->GB2312.

總結:

1,MSSQL Server不支援UTF-8(Codepage=65001)直接顯示,資料庫查詢的顯示資料使用預設的Codepage,如簡體中文版就是936,繁體中文是950,韓文949等,是以從使用的Web頁面UTF-8送出的資料自動轉換為所用的Codepage顯示,是以就是亂碼,這個有待MS SQL Server進一步發展,因為現在Oracle和MySQL是可以支援直接UTF-8存儲顯示,國際化時請先将資料由UTF-8編碼轉化為MSSQL資料庫預設的編碼,讀寫出來這個過程着相反進行轉化,這個過程看起來會因為轉化過程影響處理速度(抉擇于國際化);

2,ASP/ASPX/JSP/PHP使用MSSQL Server程式設計支援UTF-8都會面臨這樣的問題,可以看看MSDN有關這個方面的解釋

補充:根據測試和MSDN分析,将UTF-8轉化為GB2312并不是很好的方案,這樣會使包含韓文、阿拉伯文等等,這些都會變成問号,是以整個過程并不是很繁瑣,簡化一下: 寫資料庫:浏覽器表單送出資料(UTF-8)(ANSI編碼)-〉應用伺服器端程式(進行UTF-8-〉Unicode)-〉MS SQL Server(自動轉化為字元集936編碼顯示内容,但資料肯定是Unicode方式存儲的); 讀資料庫:MS SQL Server(936 - Unicode)-〉應用伺服器端程式(進行Unicode-〉UTF-8)-〉浏覽器顯示 注意:其他條件設定不變   兩個使用的函數: 1,UTF8轉化為Unicode,inline為了編譯後更快運作,老用到了,傳回字元串為了使用鍊式表達式 inline WCHAR  *UTF8ToUnicode(const char *str) throw()

 {

  int i = MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,str,-1,NULL,0);        

  WCHAR   *strUnicode=new   WCHAR[i];        

  MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,str,-1,strUnicode,i);

  return strUnicode;

  delete []strUnicode;

 } 一定要傳回WCHAR 或wchar_t類型,否則有些字元就會變成“?”,Unicode(UCS-2)是2個位元組寬   2,Unicode轉化為UTF8,inline同上意義 inline char *UnicodeToUTF8(const WCHAR* pText) throw()

 {

  int i= WideCharToMultiByte(CP_UTF8,0,pText,-1,NULL,0,NULL,NULL); //輸入緩沖區大小是寬字元數        

  char   *strUTF8   =   new   char[i];        

  WideCharToMultiByte(CP_UTF8,0,pText,-1,strUTF8,i,NULL,NULL);

  return strUTF8;

  delete []strUTF8;

 }  

      ————————————————————————————————————————————

MSDN參考( http://support.microsoft.com/kb/232580/zh-cn):

概要

某些應用程式 (尤其是基于 Web) 必須處理是用 UTF-8 編碼方法編碼 Unicode 資料。 SQL Server 7.0 和 SQL Server 2000 使用 Unicode 編碼 (UCS-2) 不同和不識别 UTF-8 作為有效字元資料。 本文讨論一些選項用于處理與此情況。

更多資訊

可以以許多不同方式進行編碼 Unicode 資料。 UCS-2 和 UTF-8 是兩種常見方法來存儲表示 Unicode 字元位模式。 作為 UCS-2 MicrosoftWindowsNT、 SQLServer、 Java、 COM, 和 SQLServerODBC 驅動程式和 OLEDB 提供所有内部表示 Unicode 資料。

用于使用 SQL Server 7.0 或 SQL Server 2000 作為後端伺服器對于應用程式, 發送和接收 Unicode 資料是以 UTF-8 編碼選項包括: 1. 如果應用程式使用 Active Server Pages (ASP) 并且您使用 Internet Information Server (IIS) 5.0 和 Microsoft Windows 2000, " < % Session.Codepage=65001 % > " 添加到您的伺服器端 ASP 腳本。 這訓示 IIS 以轉換所有動态生成字元串 (Response.Write) 從 UCS-2 以 UTF-8 它們發送到用戶端之前自動示例:。

如果您不想啟用會話, 也可以使用伺服器端指令 " < > % @ CodePage " = 65001 %。

自動從用戶端發送到伺服器通過 GET 或 POST 任何 UTF-8 資料還轉換為 UCS-2。 Session.Codepage 屬性是推薦方法以處理 Web 應用程式中 UTF-8 資料。 IIS 4.0 和 Windows NT 4.0 上沒有此 Codepage 設定。 有關其他資訊, 請參閱下列 Microsoft 知識庫文章:

254313 (http://support.microsoft.com/kb/254313/EN-US/) 錯誤消息: ActiveServerPages 錯誤 ASP 0203 ' ' 無效代碼

2. 根據應用程式中翻譯與 UCS-2 或 UTF-8。 對于該類型轉換的示例代碼是在 Unicode 聯合會的站點位于:

http://www.unicode.org/Public/PROGRAMS/CVTUTF (http://www.unicode.org/Public/PROGRAMS/CVTUTF)

Internet Request For Comments 文檔 RFC2279 中可以找到要轉換為 UTF-8 UCS-2 算法的進階說明。

在 WindowsNT 或 Windows 2000, 您可能使用 Win 32 函數 MultiByteToWideChar 和 WideCharToMultiByte 來通過傳遞常量 CP_UTF8 UTF-8 轉換與 UCS-2 作為第一個參數對函數 (65001)。

3. 修改應用程式以使用 UCS-2 代替 UTF-8 編碼。

4. 使用 BINARY / VARBINARY / IMAGE 列在伺服器上存儲實際 UTF-8 資料。 SQLServer 上存儲 UTF-8 資料意味着您可不使用 SQLServer 來排序或查找資料一樣有效字元資料的這些值範圍。 類型的列包含 UTF-8 資料不會傳回預期結果包括 " ORDERBY ", 更上 - 比操作 < > 并小于 "-" 比 " 比較和内置 SQLServer 字元串處理函數如 SUBSTRING() "。

但是, 相等比較, 工作隻要等效字元串比較是在位元組級别。 注意: 您如果 UTF-8 資料存儲在 SQLServer 應該不使用字元列 (CHAR/NCHAR / VARCHAR 和等等)。 UTF-8 是無效字元資料到 SQLServer, 和通過非字元資料存儲在字元列可能遇到問題如以下 Microsoft 知識庫文章中讨論問題:

155723 (http://support.microsoft.com/kb/155723/EN-US/) INF: SQLServer 截斷的 DBCS 字元串

234748 (http://support.microsoft.com/kb/234748/EN-US/) PRB: SQLServerODBC 驅動程式将語言事件轉換為 Unicode

如果考慮此選項, 請記住您将需要執行從 UTF-8 轉換為 UCS-2 如 ODBC、 OLEDB、 COM, 此應用程式中如果曾經需要通路 UTF-8 資料從 Web 浏覽器 (例如, 從非基于 Web 的 ODBC 應用程式) 以外任何應用程式存儲在 SQLServer Win32API 調用、 VB 和 C 運作時字元串處理函數不能使用 UTF-8 資料。 這将是翻譯負擔移到其他應用程式。

5. 如果要求不包含需要存儲資料的語言是無法滿足由單個代碼頁, 組合可能不需要使用 Unicode。

Unicode 支援引入到 SQLServer 以 SQL Server 7.0。 因為 SQL Server 6.5 不支援的 Unicode 資料, 存儲僅選項對于 SQL Server 6.5 列于步驟 4 和步驟 5。