1.三種編碼的回顧
Ansi字符串我們最熟悉,英文占一個字節,漢字2個字節,以一個\0結尾,常用於txt文本文件。
Unicode字符串,每個字符(漢字、英文字母)都占2個字節;在VC++的世界里,Microsoft比較鼓勵使用Unicode,如wchar_t。
UTF8是Unicode一種壓縮形式,英文A在unicode中表示為0x0041,英語中這種存儲方式太浪費,因為浪費了50%的空間,於是就把英文壓縮成1個字節,成了utf8編碼;但是漢字在utf8中占3個字節,顯然用做中文不如ansi合算,這就是中國的網頁用作ansi編碼而國外的網頁常用utf8的原因。程序中把15.7M大小UTF8格式的txt文件轉化為ANSI后,大小僅為10.8M。
2.轉換函數
一般情況下,可以通過Windows頭文件下的兩個函數實現各個類型之間的轉換。頭文件添加:
#include <Windows.h>
多字節字符集 -> Unicode字符集
int MultiByteToWideChar( __in UINT CodePage, // 標識了與多字節關聯的一個代碼頁值 __in DWORD dwFlags, // 允許我們進行額外的控制,它會影響帶變音符號(比如重音)的字符。但是一般情況下不適用,賦為 0 即可。 __in LPCSTR lpMultiByteStr, // 參數指定要轉換的字符串 __in int cbMultiByte, // 指定要轉換串長度的長度(字節數),如果參數值是-1,函數便可自動判斷源字符串的長度 __out LPWSTR lpWideCharStr, // 指定轉換后Unicode版本的字符串內存地址 __in int cchWideChar // 指定 lpWideCharStr 緩沖區的最大長度。 // 如果傳入0,函數不會進行轉換,而是返回一個寬字符數(包括終止字符'\0'), // 只有當緩沖區能夠容納該數量的寬字符時,轉換才會成功。 );
Unicode字符集 –> 多字節字符集
int WideCharToMultiByte( __in UINT CodePage, // 標志了要與新轉換的字符串關聯的代碼頁 __in DWORD dwFlags, // 制定額外的轉換控制,一般不需要進行這種程度的控制,而為 dwFlag 傳入 0 __in LPCWSTR lpWideCharStr, // 指定要轉換的字符串的內存地址 __in int cchWideChar, // 指出該字符串的長度,如果傳入 -1 ,則由函數來判斷字符串的長度 __out LPSTR lpMultiByteStr, // 轉換后的緩沖區 __in int cbMultiByte, // 指定 lpMultiByteStr 緩沖區的最大大小(字節數),如果傳入 0 ,函數返回該目標緩沖區需要的大小 __in LPCSTR lpDefaultChar, __out LPBOOL lpUsedDefaultChar // 寬字符字符串中,如果至少有一個字符不能轉換為對應的多字節形式,函數就會把這個變量設為 TRUE 。如果所有字符都能成功轉換,就會把這個變量設為 FALSE。 通常將此函數傳入 NULL 值。 );
只有一個字符在 CodePage 制定的代碼頁中沒有對應的表示時,WideCharToMultiByte 才會使用后兩個參數。在遇到一個不能轉換的字符時,函數便使用 lpDefaultChar 參數指向的字符。如果這個參數指向為 NULL ,函數就會使用一個默認的字符。這個默認的值通常是一個問號。這對文件操作是非常危險的,因為問號是一個通配符。
3.程序實現
程序的頭文件:
/* *作者:侯凱 *說明:utf8、unicode、utf8相互轉化 *日期:2013-6-4 */ #include <iostream> #include <string> #include <fstream> #include <Windows.h> //Windows頭文件 using std::string; using namespace std;
ANSI轉Unicode
void AnsiToUnicode() { char* sAnsi = "ANSI to Unicode, ANSI 轉換到 Unicode"; //ansi to unicode int sLen = MultiByteToWideChar(CP_ACP, NULL, sAnsi, -1, NULL, 0); wchar_t* sUnicode = new wchar_t[sLen]; //wchar_t* sUnicode = (wchar_t*)malloc(sLen*sizeof(wchar_t)); MultiByteToWideChar(CP_ACP, NULL, sAnsi, -1, sUnicode, sLen); ofstream rtxt("ansitouni.txt"); rtxt.write("\xff\xfe",2);//原因參見上一篇——"小尾"字節序方式存儲 rtxt.write((char*)sUnicode, sLen*sizeof(wchar_t)); rtxt.close(); delete[] sUnicode; sUnicode =NULL; //free(sUnicode); }
Unicode轉ANSI
void UnicodeToAnsi() { wchar_t *sUnicode = L"Convert Unicode to ANSI, Unicode 轉換為 ANSI"; //unicode to ansi int sLen = WideCharToMultiByte(CP_ACP, NULL, sUnicode, -1, NULL, 0, NULL, NULL); char* sAnsi = new char[sLen]; //char* sAnsi = (char*)malloc(sLen); WideCharToMultiByte(CP_ACP, NULL, sUnicode, -1, sAnsi, sLen, NULL, NULL); ofstream rtxt("unitoansi.txt"); rtxt.write(sAnsi, sLen); rtxt.close(); delete[] sAnsi; sAnsi =NULL; //free(sAnsi); }
Unicode轉UTF8
void UnicodeToUtf8() { wchar_t *sUnicode = L"Convert Unicode to UTF8, Unicode 轉換為 UTF8"; // unicode to UTF8 int sLen = WideCharToMultiByte(CP_UTF8, NULL, sUnicode, -1, NULL, 0, NULL, NULL); //UTF8雖然是Unicode的壓縮形式,但也是多字節字符串,所以可以以char的形式保存 char* sUtf8 = new char[sLen]; //unicode版對應的strlen是wcslen WideCharToMultiByte(CP_UTF8, NULL, sUnicode, -1, sUtf8, sLen, NULL, NULL); ofstream rtxt("unitoutf8.txt"); rtxt.write("\xef\xbb\xbf", 3);//原因參見上一篇 rtxt.write(sUtf8, sLen); rtxt.close(); delete[] sUtf8; sUtf8 =NULL; }
UTF8轉Unicode
void Utf8ToUnicode() { //UTF8 Convert to Unicode, UTF8 轉換為 Unicode,用UE十六進制打開“轉化為”直接復制過來亂碼,用16進制表示 char* sUtf8 = "UTF8 Convert to Unicode, UTF8 \xe8\xbd\xac\xe6\x8d\xa2\xe4\xb8\xba Unicode"; //UTF8 to Unicode int sLen = MultiByteToWideChar(CP_UTF8, NULL, sUtf8, -1, NULL, 0); wchar_t* sUnicode = new wchar_t[sLen]; MultiByteToWideChar(CP_UTF8, NULL, sUtf8, -1, sUnicode, sLen); ofstream rtxt("utf8touni.txt"); rtxt.write("\xff\xfe",2); rtxt.write((char*)sUnicode, sLen*sizeof(wchar_t)); rtxt.close(); delete[] sUnicode; sUnicode =NULL; }
Ansi轉換utf8和utf8轉換Ansi就是上面2個的結合,把unicode作為中間量,進行2次轉換即可。
4.UTF8轉ANSI
在網絡傳輸中,我們常常使用UTF8編碼,但在程序處理時,我們習慣於ANSI編碼,至少目前的VS2010對UTF8碼的顯示是亂碼的。以下函數綜合上述程序,實現了txt文件UTF8編碼向ANSI編碼的轉化。
//changeTxtEncoding修改字符串的編碼 char* changeTxtEncoding(char* szU8) { int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, -1, NULL, 0); wchar_t* wszString = new wchar_t[wcsLen]; ::MultiByteToWideChar(CP_UTF8, NULL, szU8, -1, wszString, wcsLen); cout<<wszString<<endl; int ansiLen = ::WideCharToMultiByte(CP_ACP, NULL, wszString, -1, NULL, 0, NULL, NULL); //wcslen(wszString) char* szAnsi = new char[ansiLen]; ::WideCharToMultiByte(CP_ACP, NULL, wszString, -1, szAnsi, ansiLen, NULL, NULL); delete[] wszString; return szAnsi; } void changeTextFromUtf8ToAnsi(const char* filename) { ifstream infile; string strLine=""; string strResult=""; infile.open(filename); infile.seekg(3, ios::beg); if (infile) { while(!infile.eof()) { getline(infile,strLine); strResult+=strLine+"\n"; } } infile.close(); char* changeTemp=new char[strResult.length()+1]; changeTemp[strResult.length()]='\0'; //問題記錄 strcpy(changeTemp, strResult.c_str()); //const char*轉化char*的方法 char* changeResult=changeTxtEncoding(changeTemp); strResult=changeResult; ofstream outfile; outfile.open("ANSI.txt"); outfile.write(strResult.c_str(), strResult.length()); outfile.flush(); outfile.close(); delete[] changeResult; delete[] changeTemp; }
問題記錄:
關於字符串的長度a.String類型的length()和size()函數都返回字符串的真實大小,不包括'\0‘ ;
b.char*類型的strlen()函數也是返回字符串的真實大小,不包括'\0‘ ;
c.注意,sizeof()函數包含'\0‘ ,如char str[] = “Hello” ;則sizeof (str ) = 6。
