字節碼問題–wchar和char的區別以及wchar和char之間的相互轉換字符編碼轉換等方法及函數介紹

出處:http://www.cnblogs.com/MichaelOwen/articles/2128771.html

收集了一些關於字符/寬字符的一些資料,在此與大家一起分享。
win2下wchar佔2個字節   linux下wchar佔4個字節
wchar_t的高位字節應該存儲在char數組的低位字節。
在 C 語言中, char 類型永遠都是一個字節, 雙字節字符類型是 wchar_t;但它不是內置類型, 定義在 stddef.h.
給 wchar_t 類型的字符或字符數組(也就是字符串)賦值要冠以 L;
格式化輸出(如 printf) wchar_t 類型的字符串, 要用 %S(而非 %s)。(我更多的是看到用ls輸出);
而且 要注意一點,printf和wprintf不能混用,否則不會打印。如:
setlocale(LC_ALL,"zh_CN.utf8");
printf("1 print chinese by wprintf test: n");
wchar_t *wstr = L"中文";
wprintf(L"%ls",wstr);
上面先使用了printf,又使用了wprintf,所以後面的wprintf無法正常輸出,只要把上面printf換成wprintf或是註釋掉就可以了。
mbs: multi byte string, 用char作為存儲類型, 一個字符可能對應1個或者多個char, 不能直接確定字符邊界. charset不確定. 過去的程序都是採用mbs的.
wcs: wide character string, 用wchar_t作為存儲類型, 一個字符對於一個wchar_t. 使用unicode編碼, charset與OS相關, 在windows平台中為UTF16(UCS-2), 在大多數unix平台中為UTF32(UCS-4).
國際化的程序都應該在內部使用wcs, 在輸入輸出時做mbs與wcs的轉換.
複製代碼
#include <stdlib.h>
#include <stdio.h>
#include <locale.h> 

int main()
{
    FILE* fp ;
    setlocale(LC_ALL,"");             //要先設置語言環境
    wchar_t wchar[5] = L"相等相等";    //定義一個寬字節的變量,初始為"相等"
    fp = fopen("1.txt", "w+");        //打開文件稱奧做
    fwprintf(fp, L"%lsn", wchar);    //輸出到文件.此處一定要為%ls,不能用%s
    fclose(fp);                       //關閉文件
    wchar_t wc2[5];                   //定義第二個寬字節變量
    
    int i = 0;
    //wc開始的有很多寬字節的操作。都和str相對應。
    wcscpy(wc2, wchar);                //複製。
    int n = wcscmp(wc2, wchar);        //比較
    if (n == 0)
    {
        wprintf(L"相等n");            
    }
    else
    {
        wprintf(L"不相等n");
    }

    char str[10];                    //定義char字符。
    n = wcstombs(str, wc2, 9);       //寬字節轉換為muiltychar
    printf("1---: %sn", str);       //輸出結果

    for (i = 0; i < 5; ++i)
    {
        wc2[i] = L'1' + i;
    }
    wc2[4] = 0;

    n = wcstombs(str, wc2, 9);        //寬字節轉換為muiltychar
    printf("2---:%sn", str);         //輸出結果
    
    return 0;
}
複製代碼
mbswcs的轉換函數:
#include <locale.h>
setlocale(LC_ALL, "");

wcstombs(char * to,wchar_t * from,size_t _maxCount);
mbstowcs(wchar_t * to,char * from,size_t _maxCount);
wcstombs/mbstowcs是實現轉換的一對函數. 這兩個c的庫函數其實內部也就是對剛剛windows的api和接下來我們要說的iconv的封裝。使用時要注意,如果涉及到中文,要先setlocale,當設置好正確的locale,函數內部才能找到正確的字符集進行轉換。
wcs的charset是固定不可變的, 但是mbs的charset是可變的, 可能是ASCII, 可能是gb2312, 也可能是big5. wcstombs/mbstowcs是根據locale環境設置來決定mbs採用的charset的, 在程序中可以用setlocale來設定locale, 例如 setlocale(LC_ALL, “chinese”)
程序啟動時, locale設定為 LC_ALL=”C”, 用 setlocale(LC_ALL, “”) 就可以設置成操作系統的locale設定.
Locale決定了當前程序運行的本地化設置方式, 但是在程序中可能需要作其他charset的轉換, 例如程序本身語言設定為簡體中文, 需要做big5與unicode的轉換,或者是其他兩種charset的mbstombs轉換, 這時wcstombs/mbstowcs就不能勝任了.
複製代碼
/*
linux 下 wchar_t和char的相互轉化和測試
win32下wchar_t佔2個字節,linux下wchar_t佔4個字節。wchar_t的高字節應該存放在char數組的低字節。
Linux下面的沒有命名為 WideCharToMultiByte() 和 MultiByteToWideChar() 函數,WideCharToMultiByte,MultiByteToWideChar是windows下的函數,
在linux下也有類似的兩個函數:
mbstowcs()
wcstombs() 
值得注意的是:
size_t mbstowcs(wchar_t *wcstr,const char *mbstr,size_t count);
這個函數的第三個參數count,大小一定要是mbstr長度的2倍,否則出來的中文也會是亂碼。

wcstombs和mbstowcs使用
#include <locale.h>
setlocale(LC_ALL, "");
wcstombs(char * to,wchar_t * from,size_t _maxCount);
mbstowcs(wchar_t * to,char * from,size_t _maxCount);

還有呢,轉碼還可以使iconv函數族,包含以下三個函數(其具體實例可以見demo_iconv.c和code_convert.c):
iconv_t iconv_open(const char *tocode, const char *fromcode);
size_t iconv(iconv_t cd,char **inbuf,size_t *inbytesleft,char **outbuf,size_t *outbytesleft);
int iconv_close(iconv_t cd);*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>


size_t get_wchar_size(const char *str)
{
   size_t len = strlen(str);
   size_t size=0;
   size_t i;
   for(i=0; i < len; i++)
   {
      if( str[size] >= 0 && str[size] <= 127 ) //不是全角字符
      size+=sizeof(wchar_t);
      else //是全角字符,是中文
      {
        size+=sizeof(wchar_t);
        i+=2;
      }
   }
  return size;
}
char *w2c(const wchar_t *pw)
{
   if(!pw)
   return NULL;

   size_t size= wcslen(pw)*sizeof(wchar_t);
   char *pc;
   if(!(pc = (char*)malloc(size)))
   {
     printf("malloc fail");
     return NULL;
   }
   wcstombs(pc,pw,size);
   return pc;

}

wchar_t *c2w(const char *pc)
{
   if(!pc)
   return NULL;

   size_t size_of_ch = strlen(pc)*sizeof(char);
   size_t size_of_wc = get_wchar_size(pc);
   wchar_t *pw;
   if(!(pw = (wchar_t*)malloc(size_of_wc)))
   {
      printf("malloc fail");
      return NULL;
   }
   mbstowcs(pw,pc,size_of_wc);
   return pw;

}
int main(void)
{
   setlocale(LC_ALL,"zh_CN.utf8");
   printf("1 print chinese by wprintf test: n");
   wchar_t *wstr = L"中文";
   //wprintf(L"%ls",wstr); //此處不要使用wprintf,因為上面已經使用了printf,而他們使用的是不同的流,不能混用   printf("%lsn",wstr); //ls

   printf("2 print chinese by printf test: n");
   char *str = "漢字";
   printf("%snnn",str); //s

   printf("3 char and wchar_t size of system test: n");
   printf("%s%dn","the size of wchar_t is : ",sizeof(wchar_t)); //4
   printf("%s%dnnn","the size of char is : ",sizeof(char));   //1

   char *pc;
   wchar_t *pw = L"中文是abc一門語言abc";
   char *tmp = "中文是abc一門語言abc";
   printf("%s%sn","input test wchar_t* ",tmp);
   pc = w2c(pw);
   printf("4 print char test after w2c (wchar to char):n");
   printf("%sn",pc);


   wchar_t *cw1 = c2w(pc);   //char to wchar
   char *wc1 = w2c(cw1);   //wchar to char
   printf("5 print char test after w2c c2w and w2c:n");
   printf("%snnn",wc1);

   char *pmc = "abc中文abc";
   wchar_t *pmw;
   printf("%s%sn","input test char* ",pmc);
   pmw = c2w(pmc);
   char *pmc1 = w2c(pmw);
   printf("6 print char test after c2w and w2c:n");
   printf("%sn",pmc1);

   printf("%lsn",wstr);
   //cout<<wstr<<endl;
   return 0;

} 
複製代碼
在unix平台中可以使用iconv來做這個轉換(iconv lib也有windows版本), 在windows平台可以用MultiByteToWideChar/WideCharToMultiByte 函數.
iconv庫的使用
利用iconv函數族進行編碼轉換(如GB2312轉換為UTF-8)
iconv函數族的頭文件是iconv.h,使用前需包含之。
#include <iconv.h>
iconv函數族有三個函數,原型如下:
(1) iconv_t iconv_open(const char *tocode, const char *fromcode);
       此函數說明將要進行哪兩種編碼的轉換,tocode是目標編碼,fromcode是原編碼,該函數返回一個轉換句柄,供以下兩個函數使用。
(2)size_t iconv(iconv_t cd,char **inbuf,size_t *inbytesleft,char **outbuf,size_t *outbytesleft);
       此函數從inbuf中讀取字符,轉換後輸出到outbuf中,inbytesleft用以記錄還未轉換的字符數,outbytesleft用以記錄輸出緩衝的剩餘空間。參數cd必須是由iconv_open函數創建的轉換描述符。
複製代碼
/*
    iconv庫的使用
    GB2312字符串轉換為UTF-8的字符串

    利用iconv函數族進行編碼轉換
    iconv函數族的頭文件是iconv.h,使用前需包含之。
    #include <iconv.h>
    iconv函數族有三個函數,原型如下:
    (1) iconv_t iconv_open(const char *tocode, const char *fromcode);
    此函數說明將要進行哪兩種編碼的轉換,tocode是目標編碼,fromcode是原編碼,該函數返回一個
    轉換句柄,供以下兩個函數使用。
    (2) size_t iconv(iconv_t cd,char **inbuf,size_t *inbytesleft,char **outbuf,size_t *outbytesleft);
    此函數從inbuf中讀取字符,轉換後輸出到outbuf中,inbytesleft用以記錄還未轉換的字符數,outbytesleft用以記錄輸出緩衝的剩餘空間。
    (3) int iconv_close(iconv_t cd);
    此函數用於關閉轉換句柄,釋放資源。*/
#include <stdio.h>
#include <stdlib.h>
#include <iconv.h>
#include <string.h>
int main(void)
{
    unsigned char *src = "魅影追擊和歌姬"; /* 需轉換的字串 */
    unsigned char dst[256] = {0}; /* 轉換後的內容 */
    unsigned char buf[1024] = {0}; /* 格式化轉換後的字串 */
    size_t src_len = strlen(src);
    size_t dst_len = sizeof(dst);
    unsigned char *in = src;
    unsigned char *out = dst;
   
    iconv_t cd;
    int i; 
    int j; 
   
    cd = iconv_open("UTF-8", "GB2312"); /* 將GB2312字符集轉換為UTF-8字符集 */
    if ((iconv_t)-1 == cd)
    {
        return -1;
    }

    printf("src: %sn", src);
    iconv(cd, &in, &src_len, &out, &dst_len); /* 執行轉換 */

    /* 以下將轉換後的內容格式化為: %XX%XX...形式的字串 */
    printf("dst: ");
    j = 0; 
    for (i = 0; i < strlen(dst); i++)
    {
        printf("%.2X ", dst[i]);
        buf[j++] = '%';
        snprintf(buf + j, 3, "%.2X", dst[i]);
        j += 2;
    }
    printf("n");
    printf("buf: %sn", buf);
   
    iconv_close(cd); /* 執行清理 */
    return 0;
}
複製代碼
大部分情形是inbuf 不為NULL,*inbuf也不為NULL。這種情況下,iconv函數將以*inbuf起始的多字節序列轉換到以*outbuf起始的多字節序列。從*inbuf開始讀取,最多*inbytesleft字節,轉換後,從*outbuf開始寫入,最多*outbytesleft字節。iconv函數一次轉換一個多字節字符,每次字符轉換,*inbuf增加已轉換的字節數,*inbytesleft相應地減少已轉換的字節數;對應地,*outbuf和*outbytesleft作相應的修改,同時修改cd的轉換狀態。
以下四種情況不能完成轉換:
1.輸入中含無效的多字節序列。此時,它將errno設置為EILSEQ並返回(size_t)(-1)。*inbuf指向無效序列的最左端。
2.輸入的字節序列已經全部被轉換過,也就是*inbytesleft減少至0。此時,iconv返回本次調用中完成轉換的數目(可逆的轉換不計入)。
3.輸入中以不完整多字節序列作結尾。此時,它將errno設置為EINVAL並返回(size_t)(-1)。*inbuf指向不完整多字節序列的最左端。
4.輸出緩存區沒有足夠空間來存儲下一個字符。此時,它將errno設置為E2BIG並返回(size_t)(-1)。
另一種情形是inbuf 為NULL或*inbuf為NULL,但*outbuf 不為NULL,*outbuf也不為NULL。這種情況下,iconv函數試圖將cd的轉換狀態設置為初始狀態並store a corresponding shift sequence at *outbuf。從*outbuf開始,最多寫入*outbytesleft字節。如果輸出緩存區沒有足夠空間來存儲這個重置後的序列,他將errno設置為E2BIG並返回(size_t)(-1)。反之,*outbuf增加寫入的字節數和*outbytesleft減少寫入的字節數。
第三種情形是inbuf 為NULL或*inbuf為NULL,*outbuf 為NULL或*outbuf為NULL。這種情況下,iconv函數試圖將cd的轉換狀態設置為初始狀態。

返回值
iconv函數返回本次調用中轉換的字符數,可逆的轉換不計入。出錯時,它將修改errno並返回(size_t)(-1)。
錯誤
除了其它錯誤以外,出現以下錯誤:
E2BIG
*outbuf沒有足夠的空間。
EILSEQ
    輸入含無效的多字節序列。
EINVAL
    輸入含不完整多字節序列。
(3) int iconv_close(iconv_t cd);
       此函數用於關閉轉換句柄,釋放資源。
這個庫使用很簡單,使用舉例:
複製代碼
int gbk2utf8(char *dst, int outlen,char *src,int inlen)
{
     static iconv_t  its_conv;
     char *p1;
     its_conv = iconv_open("UTF-8","GBK");
     const char*  p = src;
     p1 = dst;
     iconv(its_conv, &p, &inlen, &p1, &outlen);
     //printf("%sn",out);
     iconv_close(its_conv);
     return 0;
}
複製代碼
網上一個人寫的支持windows和linux的wchar_t與UTF-8編碼之間的轉換和UTF-16和wchar_t之間的轉換代碼
複製代碼
#ifdef WINDOWS  
 #include <windows.h>   
 #include <stdio.h>  
 #include <ctype.h>  #else  
 #include <iconv.h>  
 #include <wctype.h>  
 #include <wchar.h>  
 #include <errno.h>  #endif  
     
 //wchar_t轉成UTF-8  
int FW2UTF8Convert( const wchar_t* a_szSrc, int a_nSrcSize, char* a_szDest, int a_nDestSize )  
{  
     #ifdef WINDOWS  
         return WideCharToMultiByte( CP_UTF8, 0, a_szSrc, -1, a_szDest, a_nDestSize, NULL, NULL );  
     #else  
         size_t result;  
         iconv_t env;  
         env = iconv_open("UTF-8","WCHAR_T");  
         if (env==(iconv_t)-1)  
         {  
              printf("iconv_open WCHAR_T->UTF8 error%s %dn",strerror(errno),errno) ;  
              return -1;  
         }  
         result = iconv(env,(char**)&a_szSrc,(size_t*)&a_nSrcSize,(char**)&a_szDest,(size_t*)&a_nDestSize);  
         if (result==(size_t)-1)  
         {  
              printf("iconv WCHAR_T->UTF8 error %dn",errno) ;  
              return -1;  
         }  
         iconv_close(env);  
         return (int)result;  
     #endif  
}  
  
 //UTF-8轉成wchar_t  
int FUTF82WConvert( const char* a_szSrc, wchar_t* a_szDest, int a_nDestSize )  
{  
     #ifdef WINDOWS  
         return MultiByteToWideChar( CP_UTF8, 0, a_szSrc, -1, a_szDest, a_nDestSize );  
     #else  
         size_t result;  
         iconv_t env;  
         int size = strlen(a_szSrc)+1 ;  
         env = iconv_open("WCHAR_T","UTF-8");  
         if (env==(iconv_t)-1)  
         {  
              printf("iconv_open UTF8->WCHAR_T error %dn",errno) ;  
              return -1;  
         }  
         result = iconv(env,(char**)&a_szSrc,(size_t*)&size,(char**)&a_szDest,(size_t*)&a_nDestSize);  
         if (result==(size_t)-1)  
         {  
              printf("iconv UTF8->WCHAR_T error %dn",errno) ;  
              return -1;  
         }  
         iconv_close(env);  
         return (int)result;  
     #endif  
}
   
 //wchar_t轉成utf16  
int FW2UConvert( const wchar_t* a_szSrc, int  a_nSize,char* a_szDest, int a_nDestSize )  
{  
     #ifdef WINDOWS  
         memcpy_s((wchar_t*)a_szDest,a_nDestSize,a_szSrc,a_nSize);  
         return a_nSize ;  
     #else  
         size_t result;  
         iconv_t env;  
         env = iconv_open("UCS-2-INTERNAL","UCS-4-INTERNAL");  
         if (env==(iconv_t)-1)  
         {  
              printf("iconv_open WCHAR_T->UTF16 error%s %dn", strerror(errno),errno);  
              return -1;  
         }  
         result = iconv(env,(char**)&a_szSrc,(size_t*)&a_nSize,(char**)&a_szDest,(size_t*)&a_nDestSize);  
         if (result==(size_t)-1)  
         {  
              printf("iconv WCHAR_T->UTF16 error %s %dn", strerror(errno), errno);  
              return -1;  
         }  
         iconv_close(env);  
         return (int)result;  
     #endif  
}  
  
  
 //utf16轉成wchar_t  
int FU2WConvert( const  char* a_szSrc, int a_nSize, wchar_t* a_szDest, int a_nDestSize )  
{  
     #ifdef WINDOWS  
         memcpy_s(a_szDest,a_nDestSize,(const wchar_t*)a_szSrc,a_nSize);  
         return a_nSize ;  
     #else  
         size_t result;  
         iconv_t env;  
         env = iconv_open("UCS-4-INTERNAL","UCS-2-INTERNAL");  
         if (env==(iconv_t)-1)  
         {  
              printf("iconv_open error %dn",errno) ;  
              return -1;  
         }  
         result = iconv(env,(char**)&a_szSrc,(size_t*)&a_nSize,(char**)&a_szDest,(size_t*)&a_nDestSize);  
         if (result==(size_t)-1)  
         {  
              printf("UTF16 -> WCHAR_T conv error %dn",errno) ;  
              return -1;  
         }  
         iconv_close(env);  
         return (int)result;  
     #endif  
}
複製代碼
網上不錯的資源:
C/C++ 編程中多國語言處理
徹底解密C++寬字符:1、從char到wchar_t
徹底解密C++寬字符:2、Unicode和UTF
徹底解密C++寬字符:3、利用C運行時庫函數轉換
徹底解密C++寬字符:4、利用codecvt和use_facet轉換
徹底解密C++寬字符:5、利用fstream轉換
徹底解密C++寬字符:6、國際化策略
C 源文件內的中文(1)
C 源文件內的中文(2)
UTF-8 and Unicode FAQ for Unix/Linux
未經允許不得轉載:GoMCU » 字節碼問題–wchar和char的區別以及wchar和char之間的相互轉換字符編碼轉換等方法及函數介紹