Ansi字元串我們最熟悉,英文占一個位元組,漢字2個位元組,以一個/0結尾,常用于txt文本檔案
Unicode字元串,每個字元(漢字、 英文字母)都占2個位元組,以2個連續的/0結尾,NT作業系統核心用的是這種字元串,常被定義為typedef unsigned short wchar_t;是以我們有時常會見到什麼char*無法轉換為unsigned short*之類的錯誤,其實就是unicode
UTF8是 Unicode一種壓縮形式,英文A在unicode中表示為0x0041,老外覺得這種存儲方式太浪費,因為浪費了50%的空間,于是就把英文壓縮成1 個位元組,成了utf8編碼,但是漢字在utf8中占3個位元組,顯然用做中文不如ansi合算,這就是中國的網頁用作ansi編碼而老外的網頁常用utf8 的原因。
UTF8在還遊戲裡運用的很廣泛,比如WOW的lua腳本等
下面來說一下轉換,主要用代碼來說明吧
寫檔案我用了CFile類,其實用FILE*之類的也是一樣,寫檔案和字元串什麼類别沒有關系,硬體隻關心資料和長度
Ansi轉Unicode
介紹2種方法
void CConvertDlg::OnBnClickedButtonAnsiToUnicode()
{
// ansi to unicode
char* szAnsi = "abcd1234你我他";
//預轉換,得到所需空間的大小
int wcsLen = ::MultiByteToWideChar(CP_ACP, NULL, szAnsi, strlen(szAnsi), NULL, 0);
//配置設定空間要給'/0'留個空間,MultiByteToWideChar不會給'/0'空間
wchar_t* wszString = new wchar_t[wcsLen + 1];
//轉換
::MultiByteToWideChar(CP_ACP, NULL, szAnsi, strlen(szAnsi), wszString, wcsLen);
//最後加上'/0'
wszString[wcsLen] = '/0';
//unicode版的MessageBox API
::MessageBoxW(GetSafeHwnd(), wszString, wszString, MB_OK);
//接下來寫入文本
//寫文本檔案,頭2個位元組0xfeff,低位0xff寫在前
CFile cFile;
cFile.Open(_T("1.txt"), CFile::modeWrite | CFile::modeCreate);
//檔案開頭
cFile.SeekToBegin();
cFile.Write("/xff/xfe", 2);
//寫入内容
cFile.Write(wszString, wcsLen * sizeof(wchar_t));
cFile.Flush();
cFile.Close();
delete[] wszString;
wszString =NULL;
//方法2
//設定目前地域資訊,不設定的話,使用這種方法,中文不會正确顯示
//需要#include<locale.h>
setlocale(LC_CTYPE, "chs");
wchar_t wcsStr[100];
//注意下面是大寫S,在unicode中,代表後面是ansi字元串
//swprintf是sprintf的unicode版本
//格式的前面要加大寫L,代表是unicode
swprintf(wcsStr, L"%S", szAnsi);
::MessageBoxW(GetSafeHwnd(), wcsStr, wcsStr, MB_OK);
}
Unicode轉Ansi
也是2種方法
void CConvertDlg::OnBnClickedButtonUnicodeToAnsi()
{
// unicode to ansi
wchar_t* wszString = L"abcd1234你我他";
//預轉換,得到所需空間的大小,這次用的函數和上面名字相反
int ansiLen = ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
//同上,配置設定空間要給'/0'留個空間
char* szAnsi = new char[ansiLen + 1];
//轉換
//unicode版對應的strlen是wcslen
::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), szAnsi, ansiLen, NULL, NULL);
//最後加上'/0'
szAnsi[ansiLen] = '/0';
//Ansi版的MessageBox API
::MessageBoxA(GetSafeHwnd(), szAnsi, szAnsi, MB_OK);
//接下來寫入文本
//寫文本檔案,ANSI檔案沒有BOM
CFile cFile;
cFile.Open(_T("1.txt"), CFile::modeWrite | CFile::modeCreate);
//檔案開頭
cFile.SeekToBegin();
//寫入内容
cFile.Write(szAnsi, ansiLen * sizeof(char));
cFile.Flush();
cFile.Close();
delete[] szAnsi;
szAnsi =NULL;
//方法2
//和上面一樣有另一種方法
setlocale(LC_CTYPE, "chs");
char szStr[100];
//注意下面是大寫,在ansi中,代表後面是unicode字元串
//sprintf
sprintf(szStr, "%S", wszString);
::MessageBoxA(GetSafeHwnd(), szStr, szStr, MB_OK);
}
Unicode轉UTF8
void CConvertDlg::OnBnClickedButtonUnicodeToU8()
{
// unicode to UTF8
wchar_t* wszString = L"abcd1234你我他";
//預轉換,得到所需空間的大小,這次用的函數和上面名字相反
int u8Len = ::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
//同上,配置設定空間要給'/0'留個空間
//UTF8雖然是Unicode的壓縮形式,但也是多位元組字元串,是以可以以char的形式儲存
char* szU8 = new char[u8Len + 1];
//轉換
//unicode版對應的strlen是wcslen
::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), szU8, u8Len, NULL, NULL);
//最後加上'/0'
szU8[u8Len] = '/0';
//MessageBox不支援UTF8,是以隻能寫檔案
//接下來寫入文本
//寫文本檔案,UTF8的BOM是0xbfbbef
CFile cFile;
cFile.Open(_T("1.txt"), CFile::modeWrite | CFile::modeCreate);
//檔案開頭
cFile.SeekToBegin();
//寫BOM,同樣低位寫在前
cFile.Write("/xef/xbb/xbf", 3);
//寫入内容
cFile.Write(szU8, u8Len * sizeof(char));
cFile.Flush();
cFile.Close();
delete[] szU8;
szU8 =NULL;
}
UTF8轉UNICODE
void CConvertDlg::OnBnClickedButtonU8ToUnicode()
{
//UTF8 to Unicode
//由于中文直接複制過來會成亂碼,編譯器有時會報錯,故采用16進制形式
char* szU8 = "abcd1234/xe4/xbd/xa0/xe6/x88/x91/xe4/xbb/x96/x00";
//預轉換,得到所需空間的大小
int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);
//配置設定空間要給'/0'留個空間,MultiByteToWideChar不會給'/0'空間
wchar_t* wszString = new wchar_t[wcsLen + 1];
//轉換
::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);
//最後加上'/0'
wszString[wcsLen] = '/0';
//unicode版的MessageBox API
::MessageBoxW(GetSafeHwnd(), wszString, wszString, MB_OK);
//寫文本同ansi to unicode
}
Ansi轉換utf8和utf8轉換Ansi就是上面2個的結合,把unicode作為中間量,進行2次轉換即可
================
使用C++标準庫的iostream,可以友善地将控制台、檔案、字元串以及其它可擴充的外部表示作為流來處理,但要進行中文,卻會碰到很多問題。 本人原來沒怎麼用過這個iostream,這幾天嘗試用這個寫點東西,一會兒不能輸出中文,一會兒不支援中文檔案名的,搞得頭大。網上搜了搜,沒有發現适 用于所有情況的解決方案。不過後來自己經過多次測試,基本解決了這些問題,現在寫成文字作為一個總結,也供碰到同樣問題的朋友參考。關于C語言中的 printf和wprintf的中文輸出,本文也進行了探讨。
需要說明的是,我的開發環境是VS 2005(标準庫當然也是微軟實作的),不保證其它環境下是相同的效果。
1、cout和wcout在預設的C locale下,cout可以直接輸出中文,但對于wcout卻不行(至少VS 2005下不行)。對于wcout,需要将其locale設為本地語言才能輸出中文:
wcout.imbue(locale(locale(),"",LC_CTYPE)); // ①
也有人用如下語句的,但這會改變wcout的所有locale設定,比如數字“1234”會輸出為“1,234”。
wcout.imbue(locale(""));
2、ofstream和wofstream在預設的C locale下,ofstream能正确輸出中文到檔案中,但不支援中文檔案名;wofstream支援中文檔案名,但不能向檔案中輸出中文。要解決這個 問題,需要在打開檔案之前将全局locale設為本地語言。将全局locale設為本地語言後,ofstream和wofstream的問題都解決了,但 cout和wcout卻不能輸出中文了。要讓cout和wcout輸出中文,需要将全局locale恢複原來的設定,如下所示:
locale &loc=locale::global(locale(locale(),"",LC_CTYPE)); // ②
ofstream ofs("ofs測試.txt");
wofstream wofs(L"wofs測試.txt");
locale::global(loc); // ③
ofs<<"test測試"<<1234<<endl;
wofs<<L"Another test還是測試"<<1234<<endl;
3、printf和wprintf加上這兩位C語言中的老兄,問題更加複雜。考慮如下語句(注意s的大小寫):
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。
總結總之,用iostream、printf/wprintf輸出中文,有點麻煩。概括起來要點如下:
- 如果要用wcout,需要在使用之前按語句①将其locale設定為本地語言;
- 如果要用ofstream或wofstream,要在打開檔案之前按語句②将全局locale設為本地語言并儲存初始的全局locale。然後在打開檔案之後,按語句③将全局locale恢複為初始值;
- 不要混用iostream和printf/wprintf。如果要混用,隻用printf/wprintf輸出MultiByte字元串;
- 單獨使用printf/wprintf時,如果要輸出Unicode字元串,需要按語句⑧設定C語言的全局locale。如果隻輸出MultiByte字元串,則不需設定。
在wprintf()函數裡面,調用了WideCharToMultiByte()函數,就是通過目前的Locale将Unicode的字元轉換成本地字元。在不使用setlocale()的時候,wprintf()會使用預設的“C”locale判斷給定的寬字元是否<255,如果大于255就傳回錯誤,而不會進行顯示。這也就是在沒有setlocale()的情況下wprintf((wchar_t*)paName)不能顯示的真正原因。我覺得,如果wprintf()不進行這種判斷而直接将字元串給Console的話,就能夠正确顯示了