wchar和char的瞭解以及相互轉化

今天遇到wchar和char字符轉換的問題,花費了不少的時間。

typedef struct tagSerialData
{
    nuWCHAR serialdata[10];
    nuUINT  dataLength;
}SERIAL_DATA, *pSERIAL_DATA;

這是我定義的結構體(nuWCHAR是公司自定義的,相當於WCHAR),但是另外公司的同事把結構體定義成了

struct tagSerialData
{
    char serialdata[10];
    int  dataLength;
}SERIAL_DATA, *pSERIAL_DATA;

所以導致我將自己定義的結構體的dataLength賦值成10時,聯調的同事接收到的Length=0;或者我將自己定義的結構體的serialdata賦值成“1234567890”時,同事接收到的Length是一個無限大的數

這是我給自己定義的結構體賦值,獲取長度
SERIAL_DATA s_stcSerialData = {0};
wcscpy(s_stcSerialData.serialdata,L"1234567890";
s_stcSerialData.dataLength=wcslen(s_stcSerialData.serialdata)

後來發現定義的結構體不一樣時,我只好修改自己的結構體,變成nuCHAR,但是在後面的功能上我要用的是WCHAR類型的數據。這就需要我將他後來傳給我的char類型的數據轉換成WCHAR .

我在網上搜的wchar轉化成char的例子如下:

 int MultiByteToWideChar(
     UINT uCodePage, //標識了與多字節字符串關聯的一個代碼值  一般不用  傳0???
     DWORD   dwFlags,          //允許我們額外的控制,它會影響帶變音符號(比如重音) 一般不用 傳0
     PCSTR   pMultiByteStr,    //  源多字節字符串
     int cbMulitByte,              //源多字節字符串的長度(字符)  如果傳進-1,函數便可以自動判斷源串長度
     PWSTR   pWideCharStr, //轉換後的串的指針 即緩衝區的首地址
     int cchWideChar  );        //指定這個緩衝區的最大長度(字符數),如果傳入0,則函數不會轉換,而是返回一個寬字符數(包括終止字符'\0'),只有當緩衝區能夠容納該數量的寬字符時,轉換纔會成功。

  1:  以下爲將一個多字節串轉化成Unicode形式的步驟:
       (1)、調用MultiByteToWideChar,爲pWideCharStr傳入NULL,爲cchWideChar傳入0,爲pMultiByteStr傳入-1
       (2)、假設上次調用的返回值爲n ,開闢一塊緩衝區,大小爲  n *sizeof( wchar_t)
       (3)、再調用一次MultiByteToWideChar,pWideCharStr爲開闢的緩衝區的首地址,cchWideChar  爲n
       (4)、使用轉換後的字符串
       (5)、釋放緩衝區
代碼:
            char str1[100] = "1234567890";
             int numChar = ::MultiByteToWideChar(0,0,str1,-1,NULL,0);
             wchar_t *str2 = (wchar_t*)malloc( numChar*sizeof(wchar_t) );
             ::MultiByteToWideChar(0,0,str1,-1,str2,numChar);
             ::wprintf_s(str2);


2:unicode轉換爲多字節字符串:
int   WideCharToMultiByte(
       UINT   nCodePage,        //標識了與多字節字符串關聯的一個代碼值  一般不用  傳0???
       DWORD   dwFlags,        //允許我們額外的控制,它會影響帶變音符號(比如重音) 一般不用 傳0
       PCWSTR   pWideCharStr,//源unicode字符串
       int cchWideChar,          //unicode字符數
       PSTR   pMultiByteStr,   //緩衝區首地址
       int   cbMultiByte,        //緩衝區最大的長度  防止溢出
       PCSTR   pDefaultChar,//當有一個字符不能轉換時,用該指針指向那個不能轉換的字符
       PBOOL   pfUesdDefultChar) ;//如果成功轉換 該值爲FALSE 如果有至少一個字符不能成功轉換,該值爲TURE
                                                    用該值來檢測能否轉化成功,
                              最後這兩個參數只有在碰到有一個字符不能轉化時纔用到,一般傳值NULL
使用步驟和多字節轉化爲unicode差不多,不同的是第一次調用時返回直接就是所需緩衝區的大小(字節數)!!!
代碼:
             wchar_t str1[100] = L"1234567890";
              int numChar = ::WideCharToMultiByte(0,0,str1,-1,NULL,0,NULL,NULL);
             char *str2 = (char*)malloc( numChar );
             ::WideCharToMultiByte(0,0,str1,-1,str2,numChar,NULL,NULL);
                printf(str2);
             free(str2);


這是寫的一個小例子:
#include <stdio.h>
#include <Windows.h>
int main()
{
    char *a = "ab";
    wchar_t *b = (wchar_t *)a;
    wprintf(L"%s %s\n", b, L"ab");
    sleep(1000);//作用是爲了使在屏幕上停留
    return 0;
}
結果:? ab
沒有經過轉化的wchar類型和char類型不能這樣強制轉換
#include <stdio.h>
#include <Windows.h>
int main()
{
    char *a = "ab"int numChar = ::MultiByteToWideChar(0,0,a,-1,NULL,0);
    wchar_t *b = (wchar_t*)malloc(numChar*sizeof(wchar_t));
    ::MultiByteToWideChar(0,0,a,-1,b,numChar);
    wprintf(L"%s %s\n", b, L"ab");
    free(b);
    Sleep(1000);
    return 0;
}

結果:ab  ab
成功將char轉化成wchar類型。

WCHAR和CHAR的區別:

首先,說下窄字符char了,大家都很清楚,就是8bit表示的byte,長度固定。char字符只能表示ASII碼錶中的256個字符,包括前128個可見字符和後面的128個不可見字符。

   而wchar_t則是因爲char所能表示的字符數太少(256個)而應運而生的,它的長度可以8bit,16bit,32bit,長度是與不同平臺上的c庫相關的。其實這個長度是根據指定平臺上想要用的encoding編碼方式來設定的。

   在win32 MSVC環境下,c庫中wchar_t的長度是2個byte,定義如下:
   typedef unsigned short wchar_t; /* 16 bits */
   它是按照utf-16編碼,但是因爲wchar_t定義的長度只有2個字節,所以它不能表示utf-16編碼長度爲4個字節的字符。即wchar_t只表示了utf-16的一個子集。換句話話說,就是MSVC下,wchar_t是utf-16編碼的,但是隻能表示utf-16的一個子集。按utf-16編碼時,大部分字符都以固定長度的字節 (2字節) 儲存.

    在Linux-x86的GCC環境下,c庫中wchar_t的長度爲四個字節,用UCS-4(即utf-32編碼方式)。


    wchar_t就是存儲的字符的unicode碼值的編碼值,如windows下就是unicode碼值的utf-16編碼值:
    TCHAR wide[] = L"態";
    在vs中watch爲:  [0] 24577 L'態' wchar_t,即對應的十進制爲24577,而"態"unicode表中查到的碼值爲十六進制的6001,而0x6001對應的十進制值就是24577.
    TCHAR wide[] = L"a"; 因爲a的unicode值與ASCII值一樣,爲97. 如果unicode碼值U小於0x10000,則U的UTF-16編碼就是U對應的16位無符號整數。
    所以可知,0x6001的utf-16編碼值就是0x6001。       

    wchar_t   w1= L'中';  //Unicode 編碼
    wchar_t   w2= '中';   //Ansi編碼
    printf( "%0x   %0x ",w1,w2);

    結果:
    4e2d   d6d0
    雖然同樣是賦值給wchar_t,但是不同的編碼則值是不同的。同時也說明了wchar_t不光是可以存儲Unicode寬字符,也可以存儲其它的編碼。但是如果是存儲的Ansi編碼,則按照寬字符的格式輸出的是什麼呢?

    wchar_t c= L'中';
    wcout.imbue(locale("chs"));
    wcout<<c<<endl;
   上述代碼能正常輸出'中'字

   wchar_t c= '中';
   wcout.imbue(locale("chs"));
   wcout<<c<<endl;
  上述代碼不能正常輸出'中'字,結果是什麼也沒輸出。
  所以如果是需要寬字符參數的API裏傳入值爲Ansi編碼值的wchar_t可能會得到不可預測的結果。

    c/c++標準只是聲明wchar_t是一個可以表示字符集中的任意一個字符的足夠寬的變量類型。wchar_t可以用任何encoding編碼方式來存儲這個字符,如ANSI, or UCS-2, or UCS- 4, 甚至是SCU-128,只不過我們通常是用unicode編碼方式。wchar_t是與實現相關的。

所以爲了可移植性,我們不能假定wchar_t的編碼方式,然後根據編碼方式做一些相關性操作,我們只能理解它爲一個足夠寬的字符類型。

雖然從網上使用了這種轉化的辦法,但是在自己函數上實現的過程中,還是遇到了問題:就是轉化之後還是出現了亂碼。

這是我函數中接收wchar字符串,並且將wchar字符串顯示在文本框中的函數:
vSetObjectText(ID_STATIC_SERIALDATA,CGnBaseDlg::SerialData);
vSetObjectText(ID_STATIC_MAINCTRL_VER,s_stcMainCtrlVer.wMainCtrl);
而後來才發現vSetObjectText()函數的第二個參數應該接收的是一個全局變量,而之前我在轉化之前定義的都是靜態的變量,所以才導致雖然多次寫入到文件的s_stcSerialData.serialData能讀取到正確的字符串,但是在文本框中卻出現亂碼的情況。

另外附上非常不錯的文章鏈接:

http://club.topsage.com/thread-2227977-1-1.html
http://blog.sina.com.cn/s/blog_62714d6a0100ld9z.html

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