跳轉到內容

Windows 程式設計/Unicode

來自華夏公益教科書,自由的教科書

有關 Unicode 標準的參考,請參見 Unicode

Unicode 簡介

[編輯 | 編輯原始碼]

Unicode 是一個行業標準,其目標是提供一種方法,使各種形式和語言的文字能夠被計算機編碼使用。最初,文字字元在計算機中使用位元組寬資料表示:每個可列印字元(以及許多不可列印字元或“控制”字元)都使用單個位元組實現,這允許總共 256 個字元。然而,全球化帶來了對計算機能夠適應世界各地許多不同字母的需求。

舊程式碼稱為 ASCII 或 EBCDIC,但很明顯,這些程式碼都無法處理來自世界各地的所有不同字元和字母。Unicode 是解決這個問題的方案。Windows NT 使用“寬”16 位字元集實現其許多核心功能,接近 Unicode 標準,儘管它也提供了一系列與標準 ASCII 字元相容的功能。

UNICODE 字元通常稱為“寬字元”、“通用字元”或“T 字元”。本書可能會互換使用這些術語。

變長字元

[編輯 | 編輯原始碼]

在 Unicode 之前,有一種國際化嘗試引入了具有變長字元的字串。一些字元,例如標準 ASCII 字元,將是 1 位元組長。其他字元,例如擴充套件字元集,是 2 位元組長。隨著 UNICODE 的出現,這類字元格式不再受歡迎,因為它們更難編寫,也更難閱讀。Windows 仍然維護一些功能來處理變長字串,但我們不會在這裡討論這些功能。

不幸的是,由於所需的字元數量很快超過了 65,536 個可能的 16 位值,使用寬字元的所有優勢都消失了。Windows 實際上使用稱為 UTF-16 的方法來儲存字元,其中大量字元實際上佔用 //兩個// 字,這些稱為“代理對”。這種發展是在大部分 Windows API 文件編寫之後發生的,現在很多都已過時。您永遠不應該將字串資料視為“字元陣列”,而應該始終將其視為以 null 結尾的塊。例如,始終將整個字串傳送到函式以在螢幕上繪製它,不要嘗試繪製每個字元。任何在 LPSTR 後面加上方括號的程式碼都是錯誤的。

同時,變長字元字串在稱為 UTF-8 的跨平臺標準中強勢迴歸,UTF-8 與 UTF-16 相同,但使用 8 位單元。它的主要優勢在於無需兩個 API。如果使用 UTF-8,'A' 和 'W' API 將相同,並且由於兩者都是可變大小的,因此沒有缺點。儘管大多數 Windows 程式設計師不熟悉 UTF-8,但您可能會看到更多使用非 UNICODE API 的引用。

Windows 實現

[編輯 | 編輯原始碼]

Win32 API 將其所有需要文字輸入的功能分為兩類。一些函式帶有“A”字尾(表示 ASCII),另一些則帶有“W”字尾(表示寬字元或 Unicode)。這些函式使用宏“UNICODE”進行區分。

#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif

由於這種區分,當您收到編譯器錯誤時,您將在“MessageBoxW”上收到錯誤,而不是簡單地收到“MessageBox”。在這種情況下,編譯器沒有錯誤。它只是試圖遵循一組複雜的宏。

Unicode 環境

[編輯 | 編輯原始碼]

所有需要字元字串的 Windows 函式都以這種方式定義。如果要在程式中使用 unicode,則需要在包含 windows.h 檔案之前顯式定義 UNICODE 宏。

#define UNICODE
#include <windows.h>

此外,其他庫中的某些函式要求您定義宏 _UNICODE。標準庫函式可以透過包含 <tchar.h> 檔案來提供 unicode。因此,要在專案中使用 unicode,您需要在專案中進行以下宣告。

#define UNICODE
#define _UNICODE
#include <windows.h>
#include <tchar.h>

一些標頭檔案包含如下機制,以便當兩個 UNICODE 宏之一被定義時,另一個也會自動被定義。

#ifdef UNICODE
  #ifndef _UNICODE
    #define _UNICODE
  #endif
#endif
#ifdef _UNICODE
  #ifndef UNICODE
    #define UNICODE
  #endif
#endif

如果要編寫一個使用 UNICODE 的庫,那麼在您的標頭檔案中包含此機制可能會有所幫助,這樣其他程式設計師就不必擔心包含這兩個宏。通常情況下,只定義兩個宏中的一個是一個陷阱,所以要注意!

在 C 中,要建立一個寬字元字串,您需要在字串前面加上字母“L”。以下是一個例子

char *asciimessage = "This is an ASCII string.";
wchar_t *unicodemessage = L"This is a Wide Unicode string.";

資料型別“TCHAR”定義為,如果未定義 unicode,則為 char 型別,如果定義了 UNICODE(在 tchar.h 中),則為寬型別。為了使字串在 unicode 和非 unicode 之間可移植,我們可以使用 TEXT() 宏自動將字串定義為 unicode 或非 unicode

TCHAR *automessage = TEXT("This message can be either ASCII or UNICODE!");

使用 TCHAR 資料型別和 TEXT 宏是使程式碼在不同環境之間可移植的重要步驟。

此外,TEXT 宏可以寫成

TEXT("This is a generic string");
_T("This is also a generic string");
T("This is also a generic string");

所有這三個語句都是等效的。

TEXT 宏通常這樣定義

#ifdef UNICODE
#define TEXT(t) L##t
#define _T(t) L##t
#define T(t) L##t
#else
#define TEXT(t) t
#define _T(t) t
#define T(t) t
#endif

Unicode 參考

[編輯 | 編輯原始碼]

控制字元

[編輯 | 編輯原始碼]

Unicode 字元 0 到 31(U+0000 到 U+001F)屬於 C0 控制和基本拉丁塊。它們都是控制字元。這些字元對應於 ASCII 集的前 32 個字元。

程式碼點 十進位制等效值 名稱 C 轉義
U+0000 0 空字元
U+0001 1 報頭開始
U+0002 2 文字開始
U+0003 3 文字結束
U+0004 4 傳輸結束
U+0005 5 詢問
U+0006 6 確認
U+0007 7 響鈴 '\a'
U+0008 8 退格 '\b'
U+0009 9 水平製表符 '\t'
U+000A 10 換行符 '\n'
U+000B 11 垂直製表符 '\v'
U+000C 12 換頁符 '\f'
U+000D 13 回車符 '\r'
U+000E 14 換出
U+000F 15 換入
U+0010 16 資料鏈路轉義
U+0011 17 裝置控制 1
U+0012 18 裝置控制 2
U+0013 19 裝置控制 3
U+0014 20 裝置控制 4
U+0015 21 否定確認
U+0016 22 同步空閒
U+0017 23 傳輸塊結束
U+0018 24 取消
U+0019 25 介質結束
U+001A 26 替換
U+001B 27 轉義
U+001C 28 檔案分隔符
U+001D 29 組分隔符
U+001E 30 記錄分隔符
U+001F 31 單元分隔符

注意:在 Windows API 中,控制字元的含義與命令列程式中的含義不同!通常,控制字元根本不會被處理,而是顯示為不可列印字元,例如 ExtTextOut() 和所有 GDI 函式。對於 USER 函式,例如 DrawText()、MessageBox() 和多行按鈕,'\n' 將被處理以將文字分成行。對於選單,'\a' 和 '\t' 具有特殊含義並被預處理。要響鈴,'\a' 永遠不會起作用。為此,請使用 Beep() 或 sndPlaySound()。

下一章

[編輯 | 編輯原始碼]
華夏公益教科書