UNICODE
????如果你編寫的程序是針對非英語國家的用戶,如中國、日本、東歐和中東地區,那么你一定要熟悉 UNICODE 字符集。尤其是用 Visual C++/MFC 編寫針對上述國家和地區的用戶的程序時,如果你想讓自己的應用程序得到更廣泛的用戶,那么必須考慮代碼 UNICODE 的兼容性,也就是說它既在 ASCII 模式下運行 ,也能在UNICODE 模式下運行。本文將介紹 UNICODE 的一些基本編程知識,澄清很多人(包括我自己)在這個問題上存在的模糊認識。對于任何使用 Visual C++ 和/或 MFC 編程的人來說,這篇文章肯定值得一讀。 char str[100];象下面這樣聲明函數原形: void strcpy( char *out, char *in );
wchar_t str[100];或者 void wcscpy( wchar_t *out, wchar_t *in ); ??? 此外,微軟還提供一種通過預處理指令來實現 UNICODE。每當用 Visual C++ 創建新工程時,只要確定是否支持另外一種字符集,則 AppWizard 將會在頭文件中插入預處理指令。這些指令告訴編譯器程序想要支持何種字符集。這樣在使用VC++提供的通用數據類型時,編譯器將用相應的數據類型把通用數據類型替換成所需要支持的字符集。這樣很容易將代碼重新編譯成支持其它字符集的程序。 TEXT("VCKBASE Online Journal");TEXT 宏的主要作用是當定義了 UNICODE/_UNICODE 預處理指令時,字符串被標志為雙字節字符串,否則字符串被標示為 ANSI 字符串。TEXT 的定義如下: TEXT( LPTSTR string // ANSI 或者 Unicode 字符串 ); 參數 string 為字符串指針,指向被解釋的 Unicode 或者 ANSI 字符串在文檔中 微軟提供了包括通用類型在內的幾種數據類型都與 ASCII 和 UNICODE兼容。這一點可以參考微軟在線文檔有關“通用數據類型和數據類型”的章節。 ![]() 下面通過一些簡單的例子來進一步探討 UNICODE 編程。 使用 ASCII 字符集的“Hello, World”: //********************************* //hello.cpp #include <afxwin.h> // Declare the application class class CHelloApp : public CWinApp { public: virtual BOOL InitInstance(); }; // Create an instance of the application class CHelloApp HelloApp; // Declare the main window class class CHelloWindow : public CFrameWnd { CStatic* cs; public: CHelloWindow(); }; // The InitInstance function is called each // time the application first executes. BOOL CHelloApp::InitInstance() { m_pMainWnd = new CHelloWindow(); m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); return TRUE; } // The constructor for the window class CHelloWindow::CHelloWindow() { // Create the window itself Create(NULL, "Hello World!", WS_OVERLAPPEDWINDOW, CRect(0,0,200,200)); // Create a static label cs = new CStatic(); cs->Create("hello world", WS_CHILD|WS_VISIBLE|SS_CENTER, CRect(50,80,150,150), this); } 修改上面的代碼使之支持 UNICODE 字符集,串常量必須要改成對應的 UNICODE 字符。方法是對串常量使用TEXT 宏。這個宏將告訴預處理器檢查使用什么樣的字符標準: // The constructor for the window class CHelloWindow::CHelloWindow() { // Create the window itself Create(NULL, TEXT("Hello World!"), WS_OVERLAPPEDWINDOW, CRect(0,0,200,200)); // Create a static label cs = new CStatic(); cs->Create( TEXT("hello world!"), WS_CHILD|WS_VISIBLE|SS_CENTER, CRect(50,80,150,150), this); } 當預處理器碰到通用數據類型,它便檢查 AFXWIN.H 頭文件的 _UNICODE 定義。然后根據 UNICODE 定義插入相應的的數據類型。 下面的這個例子使用 Win32 API 函數和通用數據類型設置 C 盤的卷標。 //****************** // drvsvl.cpp #include <windows.h> #include <iostream.h> void main() { BOOL success; char volumeName[MAX_PATH]; cout << "輸入新的 C 盤卷標:"; cin >> volumeName; success = SetVolumeLabel("c:\\", volumeName); if (success) cout << "成功\n"; else cout << "錯誤代碼:" << GetLastError() << endl; } 通過使用 TCHAR 數據類型,將這段代碼最上面的字符數組聲明為兩個字節的字符。TEXT 宏再次被用于字符串常量: void main() { BOOL success; TCHAR volumeName[MAX_PATH]; cout << TEXT("輸入新的 C 盤卷標: "); cin >> volumeName; success = SetVolumeLabel(TEXT("c:\\" ), volumeName); if (success) cout << TEXT("成功\n"); else cout << TEXT("錯誤代碼:") << GetLastError() << endl; } ![]() ??? Visual C++ 提供了幾種 MFC 專用的數據類型用于創建具有國際化特性的應用程序。這些定義很通用,完全可以在 UNICODE、ASCII、DBCS (雙字節字符集) 和 MBCS (多字節字符集)。由于篇幅所限,本文不打算涉及所有上面提到的這些字符集。有關它們的詳細資料請參考相關資料。MFC 提供了一種透明的方式來實現這些字符集。通用數據類型的映射到哪個字符集以及映射方式是根據工程的設置決定的,默認值為 ASCII 模式,其它幾個可選項是 MBCS、DBCS 或者 UNICODE。本文主要討論 UNICODE,所以下表中只列出了 ASCII 與 UNICODE 字符之間的映射關系:
使用表一中列出的通用數據類型,開發人員可以保證所創建的工程始終是針對一種字符集,這些通用數據類型就相當于占位符,在編譯時被特定的字節所替代,使得應用程序在 ASCII 和 UNICODE 模式下都能運行。但是,有一點要特別注意,那就是上述的通用數據類型為微軟專有,與 ANSI 標準并不兼容。有關微軟提供的這些通用數據類型詳細描述請參考 MSDN 庫文檔。 ![]() ??? 為了成功編譯支持 UNICODE 的 MFC 程序,必須使用 MFC 的 UNICODE 版本庫。該庫在定制安裝Visual C++ 時是個可選安裝項。 ??? 有一點很重要:那就是不使用 UNICODE 標準在外觀上并不影響程序的執行。也就是說,上面提到過的代碼不管設沒設置 _UNICODE 生成選項,最終都能生成正常運行的程序。當開發人員使用多個版本的Win32 API函數時才會出現問題。 ??? 在使用多個版本的 Win32 API函數(任何有字符或字符串作為參數的 Win32 API函數)時,編譯器根據是否設置 _UNICODE 指令來決定調用正確的函數。如果沒有定義_UNICODE,那么編譯器將默認調用 ASCII 版本函數。
??? 綜上所述可以看到,編譯 UNICODE 版本的程序并不難。只是在編寫代碼時記住函數調用上些微的變化。微軟為此提供的擴展是開發人員能夠以透明的方式選擇所用的字符集,為應用軟件的國際化打開了方便之門。 |