出處: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; }
mbs與wcs的轉換函數:
#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