再探C++Primer (3)關於unicode和多字節字符集

###ASCⅡ碼#程序員

在ASCⅡ碼制定的過程當中,對於代碼是六、七、8位爭議不斷,因爲可靠性的緣由,捨棄了6位,而因爲成本問題(上世紀60年代來講,8位代價是很是昂貴的),選擇了7位編碼。因而最後代碼共有26個小寫字母,26個大寫字母,10個數字,32個符號,33個控制碼,一個空格碼,共128個代碼組成了ASCⅡ碼,成爲了全球標準。windows

在小型計算機發展時期,8位一字節的標準獲得鞏固,因而決定以一個字節來儲存字符,也就是8位編碼,這樣就有了128個額外的字符來補充ASCⅡ。函數

###多字節字符集DBCS#編碼

256字符的字符集難以知足如中文、日文、韓文以及世界各個國家的各類文字需求,因而產生了多字節字符集(DBCS)。DBCS前128個代碼就是ASCⅡ碼,而較高的128箇中有些還跟隨有第二個字節,這兩個字節在一塊兒表示一個單獨的字符,如一個漢字。指針

DBCS的問題在於,有些字符是單個字節組成的,例如ASCⅡ碼,有些字符是兩個字節組成的,這致使了字符串的字符長度不能由字節數量決定,字符串的長度須要解析後才知道,每一個字節都要被檢查是否是雙字節的前導字節。code

###Unicode字符集#內存

很明顯的是,DBCS是一種很笨的解決方法,也產生了不少問題。做爲程序員,咱們知道,與其混合使用1字節和2字節代碼的DBCS,倒不如用一種全都是2字節16位編碼的字符集,這就是Unicode字符集。字符串

Unicode被認爲是「寬字符」,每一個Unicode的字符是16位而不是8位,在多字節字符集裏咱們仍處理8位字符,而在Unicode字符集裏咱們再也不處理8位字符。最開始的128個Unicode字符(0x0000到0x007F)是ASCⅡ碼,而以後的128個Unicode字符(0x0080到0x00ff)是ASCⅡ碼的擴展碼。希臘字母表從0x0370到0x03ff,漢語日語韓語從0x3000到0x9FFF代碼。擴展

Unicode的優勢是隻有一個字符集,避免了二義性,而其缺點是內存佔比是ASCⅡ的兩倍。數據類型

如今再來看char和wchar_t

char a = 'A';		//定義了一個被0x41初始化的一個字節存儲空間
char *p = "hello!";		//windows是32位系統,指針p佔用4個字節空間存儲p的地址
						//字符串hello!存儲在指針p指向的內存地址及其後6個字節,共7字節
						//包括'h''e''l''l''o''!''\0'
int s = strlen(p);		//s=6; 記住這裏

wchar_t wa = 'A';		//定義了一個被0x0041初始化的兩個字節存儲空間
wchar_t *wp = L"hello!";	//L表明了是寬字節字符,這裏,hello!包括最後的\0共佔用了14個字節 
int s = strlen(wp);		//s=1; 由於strlen接受的是8位字符,它把h也就是0x0068
						//分爲了0x68和0x00(內存存儲順序是高位高地址,低位低地址)
						//當讀完0x68後,strlen判斷讀到0x00也就是字符串結束符,就結束輸出了

wchar_t用了2個字節來存儲字符,因此大部分處理char類型的函數都不能正常工做了,可是咱們有代替的,好比strlen的寬字節版本wcslen。在strlen定義的位置,也就是STRING.H中,strlen的聲明以下:

size_t	__cdecl	strlen(cosnt char*);

而wcslen聲明在了WCHAR.H中:

size_t	__cdecl	wcslen(const wchar_t*);

因此求wp的長度應該寫爲:

int s = wcslen(wp);		//求得s=6,即爲wp中字符個數

幾乎全部的字符函數都有寬字符版本,例如printf的寬字符版本爲sprintf,這些都定義在了WCHAR.H中。

###關於TCHAR.H#

Unicode也有不少的缺點,好比空間的佔用以及某些方面的不兼容等等,咱們但願維護兩個版本,一個用ASCⅡ碼而另外一個用Unicode字符集,但並不但願建立兩套源代碼文件,因此有了TCHAR.H頭文件。TCHAR爲須要字符或字符串做爲參數的普通庫函數(例如printf,strlen,sprintf,wcslen)提供了一系列的替代名稱(例如_tprintf,_tcslen),這些名稱是「通用」的,由於他們能夠指Unicode和非Unicode版本函數。

因爲TCHAR不是標準的一部分,因此定義的每一個函數和宏都有一個下劃線前綴。

定義方式以下:

#ifdef	UNICODE
#define	_tcslen wcslen
#else
#define _tcslen strlen

以此類推其餘函數定。

THCAR.H也提供了一個TCHAR類型來解決兩個字符數據類型的問題。若是UNICODE標識符被定義了,

typedef	wchar_t	TCHAR;

不然的話,TCHAR就是char

typedef char TCHAR;

關於寬字符字符串前綴L的解決辦法以下,若是UNICODE被定義了

#define	__T(x)	L##x

預處理將L和宏參數拼接在一塊兒,例如x是hello,則L##x就是L"hello"

若是UNICODE沒有被定義,則__T宏就定義以下:

#define __T(x)	x

而後

#define	_T(x) __T(x)
#define _TEXT(x) __T(x)

因此寫字符串的時候只須要將字符串字面寫在宏內就能夠了

_TEXT("hello!");

這樣若是UNICODE被定義了,字符串就解釋爲寬字符組成的,若是沒有被定義,就解釋爲8位字符組成的。

###關於char16_t和char32_t

隨着Unicode的發展,類型wchar_t已經不足以知足需求,好比在字符串編碼時,若是有特定的符號和長度特徵的類型將頗有幫助,而wchar_t的符號和長度不一樣的C/C++庫有不一樣的規定。所以C++11新增長了char16_t和char32_t。

char16_t:無符號類型,長16位,char32_t無符號類型,長32位

前綴u和U分別指出字符字面值的類型爲char16_t和char32_t。

相關文章
相關標籤/搜索