Visual Studio——理解多字节编码与Unicode码
多字節字符與寬字節字符
char與wchar_t
我們知道C++基本數據類型中表示字符的有兩種:char、wchar_t。?
char叫多字節字符,一個char占一個字節,之所以叫多字節字符是因為它表示一個字時可能是一個字節也可能是多個字節。一個英文字符(如’s’)用一個char(一個字節)表示,一個中文漢字(如’中’)用3個char(三個字節)表示,看下面的例子。
結點如下:
ch1:s?
ch2:?
str:中
wchar_t被稱為寬字符,一個wchar_t占2個字節。之所以叫寬字符是因為所有的字都要用兩個字節(即一個wchar_t)來表示,不管是英文還是中文。看下面的例子:
void TestWchar_t() {wcout.imbue(locale("chs")); // 將wcout的本地化語言設置為中文wchar_t wch1 = L's'; // 正確 wcout << "wch1:" << wch1 << endl; wchar_t wch2 = L'中'; // 正確,一個漢字用一個wchar_t表示 wcout << "wch2:" << wch2 << endl; wchar_t wstr[2] = L"中"; // 前兩個字節(前一個wchar_t)存放漢字'中',最后兩個字節(后一個wchar_t)存放字符串結束符\0 wcout << "wstr:" << wstr << endl; wchar_t wstr2[3] = L"中國"; wcout << "wstr2:" << wstr2 << endl; }?
結果如下:
ch1:s?
ch2:中?
str:中?
str2:中國
說明:?
1. 用常量字符給wchar_t變量賦值時,前面要加L。如: wchar_t wch2 = L’中’;?
2. 用常量字符串給wchar_t數組賦值時,前面要加L。如: wchar_t wstr2[3] = L”中國”;?
3. 如果不加L,對于英文可以正常,但對于非英文(如中文)會出錯。
string與wstring
字符數組可以表示一個字符串,但它是一個定長的字符串,我們在使用之前必須知道這個數組的長度。為方便字符串的操作,STL為我們定義好了字符串的類string和wstring。大家對string肯定不陌生,但wstring可能就用的少了。
string是普通的多字節版本,是基于char的,對char數組進行的一種封裝。
wstring是Unicode版本,是基于wchar_t的,對wchar_t數組進行的一種封裝。
string 與 wstring的相關轉換:
以下的兩個方法是跨平臺的,可在Windows下使用,也可在Linux下使用。
#include <cstdlib> #include <string.h> #include <string>// wstring => string std::string WString2String(const std::wstring& ws) { std::string strLocale = setlocale(LC_ALL, ""); const wchar_t* wchSrc = ws.c_str(); size_t nDestSize = wcstombs(NULL, wchSrc, 0) + 1; char *chDest = new char[nDestSize]; memset(chDest,0,nDestSize); wcstombs(chDest,wchSrc,nDestSize); std::string strResult = chDest; delete []chDest; setlocale(LC_ALL, strLocale.c_str()); return strResult; } // string => wstring std::wstring String2WString(const std::string& s) { std::string strLocale = setlocale(LC_ALL, ""); const char* chSrc = s.c_str(); size_t nDestSize = mbstowcs(NULL, chSrc, 0) + 1; wchar_t* wchDest = new wchar_t[nDestSize]; wmemset(wchDest, 0, nDestSize); mbstowcs(wchDest,chSrc,nDestSize); std::wstring wstrResult = wchDest; delete []wchDest; setlocale(LC_ALL, strLocale.c_str()); return wstrResult; }?
?
字符集(Charcater Set)與字符編碼(Encoding)
字符集(Charcater Set或Charset):是一個系統支持的所有抽象字符的集合,也就是一系列字符的集合。字符是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。常見的字符集有:ASCII字符集、GB2312字符集(主要用于處理中文漢字)、GBK字符集(主要用于處理中文漢字)、Unicode字符集等。
字符編碼(Character Encoding):是一套法則,使用該法則能夠對自然語言的字符的一個字符集(如字母表或音節表),與計算機能識別的二進制數字進行配對。即它能在符號集合與數字系統之間建立對應關系,是信息處理的一項基本技術。通常人們用符號集合(一般情況下就是文字)來表達信息,而計算機的信息處理系統則是以二進制的數字來存儲和處理信息的。字符編碼就是將符號轉換為計算機能識別的二進制編碼。
一般一個字符集等同于一個編碼方式,ANSI體系(ANSI是一種字符代碼,為使計算機支持更多語言,通常使用 0x80~0xFF 范圍的 2 個字節來表示 1 個字符)的字符集如ASCII、ISO 8859-1、GB2312、GBK等等都是如此。一般我們說一種編碼都是針對某一特定的字符集。?
一個字符集上也可以有多種編碼方式,例如UCS字符集(也是Unicode使用的字符集)上有UTF-8、UTF-16、UTF-32等編碼方式。
從計算機字符編碼的發展歷史角度來看,大概經歷了三個階段:?
第一個階段:ASCII字符集和ASCII編碼。?
計算機剛開始只支持英語(即拉丁字符),其它語言不能夠在計算機上存儲和顯示。ASCII用一個字節(Byte)的7位(bit)表示一個字符,第一位置0。后來為了表示更多的歐洲常用字符又對ASCII進行了擴展,又有了EASCII,EASCII用8位表示一個字符,使它能多表示128個字符,支持了部分西歐字符。
第二個階段:ANSI編碼(本地化)?
為使計算機支持更多語言,通常使用 0x80~0xFF 范圍的 2 個字節來表示 1 個字符。比如:漢字 ‘中’ 在中文操作系統中,使用 [0xD6,0xD0] 這兩個字節存儲。?
不同的國家和地區制定了不同的標準,由此產生了 GB2312, BIG5, JIS 等各自的編碼標準。這些使用 2 個字節來代表一個字符的各種漢字延伸編碼方式,稱為 ANSI 編碼。在簡體中文系統下,ANSI 編碼代表 GB2312 編碼,在日文操作系統下,ANSI 編碼代表 JIS 編碼。?
不同 ANSI 編碼之間互不兼容,當信息在國際間交流時,無法將屬于兩種語言的文字,存儲在同一段 ANSI 編碼的文本中。
第三個階段:UNICODE(國際化)?
為了使國際間信息交流更加方便,國際組織制定了 UNICODE 字符集,為各種語言中的每一個字符設定了統一并且唯一的數字編號,以滿足跨語言、跨平臺進行文本轉換、處理的要求。UNICODE 常見的有三種編碼方式:UTF-8(1個字節表示)、UTF-16((2個字節表示))、UTF-32(4個字節表示)。
我們可以用一個樹狀圖來表示由ASCII發展而來的各個字符集和編碼的分支:?
?圖 1: 各種類型的編譯
?
如果要更詳細地了解字符集和字符編碼請參考:?
字符集和字符編碼(Charset & Encoding)
工程里多字節與寬字符的配制
右鍵你的工程名->Properties,設置如下:?
圖 2: Character Set
?
圖 3: Unicode
圖 4: Multi-Byte
Unicode Character Set與Multi-Byte Character Set有什么區別呢?
Unicode Character Set和Multi-Byte Character Set這兩個設置有什么區別呢?我們來看一個例子:?
有一個程序需要用MessageBox彈出提示框:
?
上面這個Demo非常簡單不用多說了吧!我們將Character Set設置為Multi-Byte Character Set時,可以正常編譯和運行。但當我們設置為Unicode Character Set,則會有以下編譯錯誤:
error C2664: ‘MessageBoxW’ : cannot convert parameter 2 from ‘const char [18]’ to ‘LPCWSTR’
這是因為MessageBox有兩個版本,一個MessageBoxW針對Unicode版的,一個是MessageBoxA針對Multi-Byte的,它們通過不同宏進行隔開,預設不同的宏會使用不同的版本。我們使用了Use Unicode Character Set就預設了_UNICODE、UNICODE宏,所以編譯時就會使用MessageBoxW,這時我們傳入多字節常量字符串肯定會有問題,而應該傳入寬符的字符串,即將”Title”改為L”Title”就可以了,”這是一個測試程序!”也一樣。
WINUSERAPI int WINAPI MessageBoxA(__in_opt HWND hWnd,__in_opt LPCSTR lpText,__in_opt LPCSTR lpCaption,__in UINT uType); WINUSERAPI int WINAPI MessageBoxW(__in_opt HWND hWnd, __in_opt LPCWSTR lpText, __in_opt LPCWSTR lpCaption, __in UINT uType); #ifdef UNICODE #define MessageBox MessageBoxW #else #define MessageBox MessageBoxA #endif // !UNICODE?
上面的Multi-Byte Character Set一般是指ANSI(多字節)字符集,關于ANSI請參考第二小節字符集(Charcater Set)與字符編碼(Encoding)。而Unicode Character Set就是Unicode字符集,一般是指UTF-16編碼的Unicode。也就是說每個字符編碼為兩個字節,兩個字節可以表示65535個字符,65535個字符可以表示世界上大部分的語言。
一般推薦使用Unicode的方式,因為它可以適應各個國家語言,在進行軟件國際時將會非常便得。除非在對存儲要求非常高的時候,或要兼容C的代碼時,我們才會使用多字節的方式 。
理解_T()、_Text()宏即L”“
上一小節對MessageBox的調用中除了使用L”Title”外,還可以使用_T(“Title”)和_TEXT(“Title”)。而且你會發現在MFC和Win32程序中會更多地使用_T和_TEXT,那_T、_TEXT和L之間有什么區別呢?
通過第一小節多字節字符與寬字節字符我們知道表示多字節字符(char)串常量時用一般的雙引號括起來就可以了,如”String test”;而表示寬字節字符(wchar_t)串常量時要在引號前加L,如L”String test”。
查看tchar.h頭文件的定義我們知道_T和_TEXT的功能是一樣的,是一個預定義的宏。
#define _T(x) __T(x) #define _TEXT(x) __T(x)?
我們再看看__T(x)的定義,發現它有兩個:
#ifdef _UNICODE // ... 省略其它代碼 #define __T(x) L ## x // ... 省略其它代碼 #else /* ndef _UNICODE */ // ... 省略其它代碼 #define __T(x) x // ... 省略其它代碼 #endif /* _UNICODE */?
這下明白了嗎?當我們的工程的Character Set設置為Use Unicode Character Set時_T和_TEXT就會在常量字符串前面加L,否則(即Use Multi-Byte Character Set時)就會以一般的字符串處理。
Dword、LPSTR、LPWSTR、LPCSTR、LPCWSTR、LPTSTR、LPCTSTR
VC++中還有一些常用的宏你也許會范糊涂,如Dword、LPSTR、LPWSTR、LPCSTR、LPCWSTR、LPTSTR、LPCTSTR。這里我們統一總結一下:?
常見的宏:
| WCHAR | wchar_t | wchar_t |
| LPSTR | char* | char* |
| LPCSTR | const char* | const char* |
| LPWSTR | wchar_t* | wchar_t* |
| LPCWSTR | const wchar_t* | const wchar_t* |
| TCHAR | char | wchar_t |
| LPTSTR | TCHAR*(或char*) | TCHAR* (或wchar_t*) |
| LPCTSTR | const TCHAR* | const TCHAR* |
相互轉換方法:?
LPWSTR->LPTSTR: W2T();?
LPTSTR->LPWSTR: T2W();?
LPCWSTR->LPCSTR: W2CT();?
LPCSTR->LPCWSTR: T2CW();
ANSI->UNICODE: A2W();?
UNICODE->ANSI: W2A();
字符串函數:?
還有一些字符串的操作函數,它們也有一 一對應關系:
| strlen(); | wcslen(); |
| strcpy(); | wcscpy(); |
| strcmp(); | wcscmp(); |
| strcat(); | wcscat(); |
| strchr(); | wcschr(); |
| … | … |
通過這些函數和宏的命名你也許就發現了一些霍規律,一般帶有前綴w(或后綴W)的都是用于寬字符的,而不帶前綴w(或帶有后綴A)的一般是用于多字節字符的。
理解CString產生的原因與工作的機理
CString:動態的TCHAR數組,是對TCHAR數組的一種封閉。它是一個完全獨立的類,封裝了“+”等操作符和字符串操作方法,換句話說就是CString是對TCHAR操作的方法的集合。它的作用是方便WIN32程序和MFC程序進行字符串的處理和類型的轉換。
關于CString更詳細的用法請參考:?
CString與string、char*的區別和轉換?
CString的常見用法
參考文章:?
字符集和字符編碼(Charset & Encoding)?
字符,字節和編碼?
《windows核心編程系列》二談談ANSI和Unicode字符集?
Dword、LPSTR、LPWSTR、LPCSTR、LPCWSTR、LPTSTR、LPCTSTR
總結
以上是生活随笔為你收集整理的Visual Studio——理解多字节编码与Unicode码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spark使用总结与分享
- 下一篇: solaris下常见文件压缩/解压方式简