windows字符類型

windows字符類型

所謂的短字符,就是用8bit來表示的字符,典型的應用是ASCII碼.而寬字符,顧名思義,就是用16bit表示的字符,典型的有UNICODE.關於windows下的ASCII和UNICODE的更多信息,可以參考這兩本經典著作:《windows 程序設計》,《windows 核心編程》.這兩本書關於這兩種字符都有比較詳細的解說.

寬字符轉換爲多個短字符是一個難點,不過我們只要掌握到其中的要領,便可如魚得水.


char:
ANSI字符串,可用字符串處理函數strcat( ),strcpy( ), strlen( )等以str打頭的函數。

wchar_t :
wchar_t是Unicode字符的數據類型,它的實際定義爲:typedef unsigned short wchar_t;
wchar_t 可用字符串處理函數:wcscat(),wcscpy(),wcslen()等以wcs打頭的函數。

WCHAR:
在頭文件中有這樣的定義:typedef wchar_t WCHAR; 所以WCHAR實際就是wchar_t。
在C語言裏面提供了_UNICODE宏(有下劃線),在Windows裏面提供了UNICODE宏(無下劃線),只要定了_UNICODE宏和UNICODE宏,系統就會自動切換到UNICODE版本,否則,系統按照ANSI的方式進行編譯和運行。只定義了宏並不能實現自動的轉換,他還需要一系列的字符定義支持。

TCHAR:
如果定義了UNICODE宏則TCHAR被定義爲wchar_t。typedef wchar_t TCHAR; 否則TCHAR被定義爲char typedef char TCHAR;

ACHAR:
此類型是AUTODESK公司在adachar.h 頭文件中定義的。
當定義了AD_UNICODE(AUTODESK公司使用UNICODE宏)時爲wchar_t。



::setlocale(LC_ALL, "chs");



MultiByteToWideChar和WideCharToMultiByte用法詳解

這個是我們需要轉化的多字節字符串:
char sText[20] = {"多字節字符串!OK!"};

我們需要知道轉化後的寬字符需要多少個數組空間.雖然在這個里程裏面,我們可以直接定義一個20*2寬字符的數組,並且事實上將運行得非常輕鬆愉快.但假如多字節字符串更多,達到上千個乃至上萬個,我們將會發現其中浪費的內存將會越來越多.所以以多字節字符的個數的兩倍作爲寬字符數組下標的聲明絕對不是一個好主意.
所幸,我們能夠確知所需要的數組空間.
我們只需要將MultiByteToWideChar()的第四個形參設爲-1,即可返回所需的短字符數組空間的個數:
DWORD dwNum = MultiByteToWideChar (CP_ACP, 0, sText, -1, NULL, 0);

接下來,我們只需要分配響應的數組空間:
wchar_t *pwText;
pwText = new wchar_t[dwNum];
if(!pwText)
{
delete []pwText;
}

接着,我們就可以着手進行轉換了.在這裏以轉換成ASCII碼做爲例子:
MultiByteToWideChar (CP_ACP, 0, psText, -1, sText, dwSize);

最後,使用完畢當然要記得釋放佔用的內存:
delete []psText;


同理,寬字符轉爲多字節字符的代碼如下:
wchar_t wText[20] = {L"寬字符轉換實例!OK!"};
DWORD dwNum = WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,NULL,0,NULL,FALSE);
char *psText;
psText = new char[dwNum];
if(!psText)
{
delete []psText;
}
WideCharToMultiByte (CP_OEMCP,NULL,lpcwszStr,-1,psText,dwNum,NULL,FALSE);
delete []psText;

如果之前我們已經分配好空間,並且由於字符串較短,可以不理會浪費的空間,僅僅只是想簡單地將短字符和寬字符相互轉換,那有沒有什麼簡便的方法呢?

WIN32 API裏沒有符合這種要求的函數,但我們可以自己進行封裝:

//-------------------------------------------------------------------------------------
//Description:
// This function maps a character string to a wide-character (Unicode) string
//
//Parameters:
// lpcszStr: [in] Pointer to the character string to be converted
// lpwszStr: [out] Pointer to a buffer that receives the translated string.
// dwSize: [in] Size of the buffer
//
//Return Values:
// TRUE: Succeed
// FALSE: Failed
//
//Example:
// MByteToWChar(szA,szW,sizeof(szW)/sizeof(szW[0]));
//---------------------------------------------------------------------------------------
BOOL MByteToWChar(LPCSTR lpcszStr, LPWSTR lpwszStr, DWORD dwSize)
{
// Get the required size of the buffer that receives the Unicode
// string.
DWORD dwMinSize;
dwMinSize = MultiByteToWideChar (CP_ACP, 0, lpcszStr, -1, NULL, 0);

if(dwSize < dwMinSize)
{
return FALSE;
}


// Convert headers from ASCII to Unicode.
MultiByteToWideChar (CP_ACP, 0, lpcszStr, -1, lpwszStr, dwMinSize);
return TRUE;
}

//-------------------------------------------------------------------------------------
//Description:
// This function maps a wide-character string to a new character string
//
//Parameters:
// lpcwszStr: [in] Pointer to the character string to be converted
// lpszStr: [out] Pointer to a buffer that receives the translated string.
// dwSize: [in] Size of the buffer
//
//Return Values:
// TRUE: Succeed
// FALSE: Failed
//
//Example:
// MByteToWChar(szW,szA,sizeof(szA)/sizeof(szA[0]));
//---------------------------------------------------------------------------------------
BOOL WCharToMByte(LPCWSTR lpcwszStr, LPSTR lpszStr, DWORD dwSize)
{
DWORD dwMinSize;
dwMinSize = WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,NULL,0,NULL,FALSE);
if(dwSize < dwMinSize)
{
return FALSE;
}
WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,lpszStr,dwSize,NULL,FALSE);
return TRUE;
}


使用方法也很簡單,示例如下:
wchar_t wText[10] = {L"函數示例"};
char sText[20]= {0};
WCharToMByte(wText,sText,sizeof(sText)/sizeof(sText[0]));
MByteToWChar(sText,wText,sizeof(wText)/sizeof(wText[0]));

這兩個函數的缺點在於無法動態分配內存,在轉換很長的字符串時可能會浪費較多內存空間;優點是,在不考慮浪費空間的情況下轉換較短字符串非常方便.

2.MultiByteToWideChar()函數亂碼的問題

有的朋友可能已經發現,在標準的WinCE4.2或WinCE5.0 SDK模擬器下,這個函數都無法正常工作,其轉換之後的字符全是亂碼.及時更改MultiByteToWideChar()參數也依然如此.
不過這個不是代碼問題,其結症在於所定製的操作系統.如果我們定製的操作系統默認語言不是中文,也會出現這種情況.由於標準的SDK默認語言爲英文,所以肯定會出現這個問題.而這個問題的解決,不能在簡單地更改控制面板的"區域選項"的"默認語言",而是要在系統定製的時候,選擇默認語言爲"中文".
系統定製時選擇默認語言的位置於:
Platform -> Setting... -> locale -> default language ,選擇"中文",然後編譯即可.

 >>>>>History of Character Sets (from Programming Windows)

  ●●The American Standard Code for Information Interchange (ASCII) had its origins in the late 1950s and was finalized in 1967.

  ●●The final code had 26 lowercase letters, 26 uppercase letters, 10 digits, 32 symbols, 33 control codes, and a space, for a total of 128 codes.

  ●●The basic problem we have here is that the world's written languages simply cannot be represented by 256 8-bit codes

  ●●Unicode is a uniform 16-bit system, thus allowing the representation of 65,536 characters. This is sufficient for all the characters and ideographs in all the written languages of the world, including a bunch of math, symbol, and dingbat collections.

  ●●ANSI C supports character sets that require more than one byte per character through a concept called "wide characters."

  ●●Wide characters aren't necessarily Unicode. Unicode is one possible wide-character encoding.

  ●●Wide characters in C are based on the wchar_t data type, which is defined in several header files, including WCHAR.H, like so:

  typedef unsigned short wchar_t ;

  To define a variable containing a single wide character, use the following statement:

  wchar_t c = `A' ;

  You can also define an initialized pointer to a wide-character string:

  wchar_t * p = L"Hello!" ;

  Similarly, you can define an array of wide characters this way:

  static wchar_t a[] = L"Hello!" ;

  The string again requires 14 bytes of storage, and sizeof (a) will return 14.

  ●●>>>>>>>>>>win32

  TCHAR.H provides a set of alternative names for the normal run-time library functions requiring string parameters (for example, _tprintf and _tcslen). These are sometimes referred to as "generic"function names because they can refer to either the Unicode or non-Unicode versions of the functions.

  TCHAR.H also solves the problem of the two character data types with a new data type named TCHAR.

  If the _UNICODE identifier is defined, TCHAR is wchar_t:

  typedef wchar_t TCHAR ;

  Otherwise, TCHAR is simply a char:

  typedef char TCHAR ;

  If the _UNICODE identifier is defined, a macro called __T is defined like this:

  #define __T(x) L##x

  If the _UNICODE identifier is not defined, the __T macro is simply defined in the following way:

  #define __T(x) x

  ●●>>>>>>>---- arx2004

  在 adesk.h 中

  // Continued use of the type "char" is prohibited. Please

  // use only the abstract type "ACHAR" below, to contain

  // character information.

  typedef char ACHAR;

  ●●>>>>>>>------arx2007

  在 adesk.h 中,有變化

  #include "AdAChar.h" // ACHAR typedef

  而在adaAChar.h 中

  typedef wchar_t ACHAR;

  //-------------------------------------------



L與_T("")區別

VC++裏面定義字符串的時候,用_T來保證兼容性。VC++支持ascii和unicode兩種字符類型,用_T可以保證從ascii編碼類型轉換到unicode編碼類型的時候,程序不需要修改。

如果將來你不打算升級到unicode,那麼也不需要_T。

_t("hello world")
在ansi的環境下,它是ansi的,如果在unicode下,那麼它將自動解釋爲雙字節字符串,既unicode編碼。
這樣做的好處,不管是ansi環境,還是unicode環境,都適用。

那麼在VC++中,字符串_T("ABC")和一個普通的字符串"ABC"有什麼區別呢?

_T("ABC")
如果定義了unicode,它將表示爲L"ABC",每個字符爲16位,寬字符串。

如果沒有定義unicode,它就是ascii的"ABC",每個字符爲8位。

相當於

#ifdef _UNICODE
#define _T("ABC") L"ABC"
#else
#define _T("ABC") "ABC"
#endif


_T("ABC")中的一個字符和漢字一樣,佔兩個字節,而在"ABC"中,英文字符佔一個字節,漢字佔兩個字節。



一、 在字符串前加一個L作用:
如 L"我的字符串" 表示將ANSI字符串轉換成unicode的字符串,就是每個字符佔用兩個字節。
strlen("asd") = 3;
strlen(L"asd") = 6;

二、 _T宏可以把一個引號引起來的字符串,根據你的環境設置,使得編譯器會根據編譯目標環境選擇合適的(Unicode還是ANSI)字符處理方式
如果你定義了UNICODE,那麼_T宏會把字符串前面加一個L。這時 _T("ABCD") 相當於 L"ABCD" ,這是寬字符串。
如果沒有定義,那麼_T宏不會在字符串前面加那個L,_T("ABCD") 就等價於 "ABCD"

三、TEXT,_TEXT 和_T 一樣的

如下面三語句:
TCHAR szStr1[] = TEXT("str1");
char szStr2[] = "str2";
WCHAR szStr3[] = L("str3");
那麼第一句話在定義了UNICODE時會解釋爲第三句話,沒有定義時就等於第二句話。
但二句話無論是否定義了UNICODE都是生成一個ANSI字符串,而第三句話總是生成UNICODE字符串。
爲了程序的可移植性,建議都用第一種表示方法。
但在某些情況下,某個字符必須爲ANSI或UNICODE,那就用後兩種方法。



常用的unicode數據類型
在PPC開發中,使用的是unicode字符集,所以會經常用到一些數據類型,以下就是一些常用類型:
WCHAR :一個unicode字符。
LPWSTR:指向一個unicode字符的指針。
LPCWSTR:指向一個unicode字符串常量的指針。
TEXT("")和L""是windows.h中定義的宏,這兩個在使用上經常是混用的,也沒有什麼區別,但是在使用時,最好還是用TEXT("")而不是L""。因爲,L""只能生成unicode字符串,而TEXT("")與目標環境相關,在winCE下生成unicode字符串,在win9X下則生成ANSI字符串,其常用簡寫爲_T("")。


關於char, wchar_t, TCHAR, _T(),L,宏 _T、TEXT,_TEXT、L

char :單字節變量類型,最多表示256個字符,

wchar_t :寬字節變量類型,用於表示Unicode字符,

它實際定義在<string.h>裏:typedef unsigned short wchar_t。

爲了讓編譯器識別Unicode字符串,必須以在前面加一個“L”,定義寬字節類型方法如下:

wchar_t c = `A' ;
wchar_t * p = L"Hello!" ;
wchar_t a[] = L"Hello!" ;

其中,寬字節類型每個變量佔用2個字節,故上述數組a的sizeof(a) = 14

TCHAR / _T( ) :
如果在程序中既包括ANSI又包括Unicode編碼,需要包括頭文件tchar.h。TCHAR是定義在該頭文件中的宏,它視你是否定義了_UNICODE宏而定義成:
定義了_UNICODE: typedef wchar_t TCHAR ;
沒有定義_UNICODE: typedef char TCHAR ;

#ifdef UNICODE
typedef char TCHAR;
#else
typede wchar_t TCHAR;
#endif
_T( )也是定義在該頭文件中的宏,視是否定義了_UNICODE宏而定義成:
定義了_UNICODE: #define _T(x) L##x
沒有定義_UNICODE: #define _T(x) x
注意:如果在程序中使用了TCHAR,那麼就不應該使用ANSI的strXXX函數或者Unicode的wcsXXX函數了,而必須使用tchar.h中定義的_tcsXXX函數。

以strcpy函數爲例子,總結一下:

Code
//如果你想使用ANSI字符串,那麼請使用這一套寫法:
char szString[100];
strcpy(szString,"test");
//如果你想使用Unicode字符串,那麼請使用這一套:
wchar_t szString[100];
wcscpy(szString,L"test");
//如果你想通過定義_UNICODE宏,而編譯ANSI或者Unicode字符串代碼:
TCHAR szString[100];
_tcscpy(szString,_TEXT("test"));

CSDN:superarhow說: 不要再使用TCHAR和_T了!他分析了原因後總結:如 果您正開始一個新的項目,請無論如何也要頂住壓力,直接使用UNICODE編碼!切記!您只需要對您的組員進行10分鐘的培訓,記住strcpy用 wcscpy,sprintf用swprintf代替,常數前加L,就可以了!它不會花您很多時間的,帶給您的是穩定和安全!相信偶,沒錯的!!

一、 在字符串前加一個L作用:
如 L"我的字符串" 表示將ANSI字符串轉換成unicode的字符串,就是每個字符佔用兩個字節。
strlen("asd") = 3;
strlen(L"asd") = 6;
二、 _T宏可以把一個引號引起來的字符串,根據你的環境設置,使得編譯器會根據編譯目標環境選擇合適的(Unicode還是ANSI)字符處理方式
如果你定義了UNICODE,那麼_T宏會把字符串前面加一個L。這時 _T("ABCD") 相當於 L"ABCD" ,這是寬字符串。
如果沒有定義,那麼_T宏不會在字符串前面加那個L,_T("ABCD") 就等價於 "ABCD"
三、TEXT,_TEXT 和_T 一樣的
如下面三語句:
TCHAR szStr1[] = TEXT("str1");
char szStr2[] = "str2";
WCHAR szStr3[] = L("str3");
那麼第一句話在定義了UNICODE時會解釋爲第三句話,沒有定義時就等於第二句話。
但二句話無論是否定義了UNICODE都是生成一個ANSI字符串,而第三句話總是生成UNICODE字符串。
爲了程序的可移植性,建議都用第一種表示方法。
但在某些情況下,某個字符必須爲ANSI或UNICODE,那就用後兩種方法。



四、修改字符串運算問題

 一些字符串操作函數需要獲取字符串的字符數(sizeof(szBuffer)/sizeof(TCHAR)),而另一些函數可能需要獲取字符串的字節數sizeof(szBuffer)。您應該注意該問題並仔細分析字符串操作函數,以確定能夠得到正確的結果。
ANSI操作函數以str開頭,如strcpy(),strcat(),strlen();
Unicode操作函數以wcs開頭,如wcscpy,wcscpy(),wcslen();

MBCS 操作函數以_mbs開頭 _mbscpy
ANSI/Unicode操作函數以_tcs開頭 _tcscpy(C運行期庫); TCHAR
ANSI/Unicode操作函數以lstr開頭 lstrcpy(Windows函數);
考慮ANSI和Unicode的兼容,我們需要使用以_tcs開頭或lstr開頭的通用字符串操作函數。

所有新的和未過時的函數在Windows2000中都同時擁有ANSI和Unicode兩個版本。ANSI版本函數結尾以A表示;Unicode版本函數結尾以W表示。Windows會如下定義:
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif // !UNICODE


字符集 實例
ANSI “string”
Unicode L“string”
ANSI/Unicode T(“string”)或_TEXT(“string”)if( szError[0] == _TEXT(‘J’) ){ }

五、printf和wprintf

printf("%s", "multibyte中文\n"); // ④
printf("%S", L"unicode中文\n"); // ⑤
wprintf(L"%S", "multibyte中文\n"); // ⑥
wprintf(L"%s", L"unicode中文\n"); // ⑦

  缺省情況下,⑤、⑦兩條語句不能輸出中文,這兩條語句中字符串的形式是unicode形式的。如果在所有輸出語句之前加上如下語句將C語言的全局locale設置爲本地語言(C語言中只有全局locale)就可以正常輸出了:

  setlocale(LC_CTYPE, ""); // ⑧

  但這會導致cout和wcout不能輸出中文(汗,的確麻煩),將C語言的全局locale恢復後cout和wcout就正常了,如下所示:

  setlocale(LC_CTYPE, "C"); // ⑨

   但恢復後,printf和wprintf輸出Unicode文本又不正常了(輸出MultiByte文本總是正常的)。總不能每寫一個 printf/wprintf就設置一次然後再恢復一次吧?所以,建議不要混用iostream和printf/wprintf,實在要混用,那就讓 printf/wprintf只輸出MultiByte字符串,這樣不需要調用setlocale(),也就不會影響到cout和wcout。

如果要用wcout,需要在使用之前按語句①將其locale設置爲本地語言;
如果要用ofstream或wofstream,要在打開文件之前按語句②將全局locale設爲本地語言並保存初始的全局locale。然後在打開文件之後,按語句③將全局locale恢復爲初始值;
不要混用iostream和printf/wprintf。如果要混用,只用printf/wprintf輸出MultiByte字符串;
單獨使用printf/wprintf時,如果要輸出Unicode字符串,需要按語句⑧設置C語言的全局locale。如果只輸出MultiByte字符串,則不需設置。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章