天天看點

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

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種方法

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

void  CConvertDlg::OnBnClickedButtonAnsiToUnicode()

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

{

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    // ansi to unicode

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    char* szAnsi = "abcd1234你我他";

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //預轉換,得到所需空間的大小

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    int wcsLen = ::MultiByteToWideChar(CP_ACP, NULL, szAnsi, strlen(szAnsi), NULL, 0);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //配置設定空間要給'/0'留個空間,MultiByteToWideChar不會給'/0'空間

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    wchar_t* wszString = new wchar_t[wcsLen + 1];

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //轉換

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    ::MultiByteToWideChar(CP_ACP, NULL, szAnsi, strlen(szAnsi), wszString, wcsLen);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //最後加上'/0'

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    wszString[wcsLen] = '/0';

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //unicode版的MessageBox API

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    ::MessageBoxW(GetSafeHwnd(), wszString, wszString, MB_OK);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //接下來寫入文本

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //寫文本檔案,頭2個位元組0xfeff,低位0xff寫在前

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    CFile cFile;

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Open(_T("1.txt"), CFile::modeWrite | CFile::modeCreate);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //檔案開頭

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.SeekToBegin();

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Write("/xff/xfe", 2);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //寫入内容

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Write(wszString, wcsLen * sizeof(wchar_t));

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Flush();

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Close();

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    delete[] wszString;

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    wszString =NULL;

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //方法2

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //設定目前地域資訊,不設定的話,使用這種方法,中文不會正确顯示

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //需要#include<locale.h>

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    setlocale(LC_CTYPE, "chs"); 

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    wchar_t wcsStr[100];

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //注意下面是大寫S,在unicode中,代表後面是ansi字元串

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //swprintf是sprintf的unicode版本

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //格式的前面要加大寫L,代表是unicode

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    swprintf(wcsStr, L"%S", szAnsi);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    ::MessageBoxW(GetSafeHwnd(), wcsStr, wcsStr, MB_OK);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

}

Unicode轉Ansi

也是2種方法

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

void  CConvertDlg::OnBnClickedButtonUnicodeToAnsi()

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

{

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    // unicode to ansi

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    wchar_t* wszString = L"abcd1234你我他";

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //預轉換,得到所需空間的大小,這次用的函數和上面名字相反

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    int ansiLen = ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //同上,配置設定空間要給'/0'留個空間

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    char* szAnsi = new char[ansiLen + 1];

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //轉換

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //unicode版對應的strlen是wcslen

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), szAnsi, ansiLen, NULL, NULL);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //最後加上'/0'

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    szAnsi[ansiLen] = '/0';

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //Ansi版的MessageBox API

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    ::MessageBoxA(GetSafeHwnd(), szAnsi, szAnsi, MB_OK);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //接下來寫入文本

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //寫文本檔案,ANSI檔案沒有BOM

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    CFile cFile;

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Open(_T("1.txt"), CFile::modeWrite | CFile::modeCreate);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //檔案開頭

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.SeekToBegin();

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //寫入内容

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Write(szAnsi, ansiLen * sizeof(char));

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Flush();

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Close();

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    delete[] szAnsi;

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    szAnsi =NULL;

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //方法2

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //和上面一樣有另一種方法

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    setlocale(LC_CTYPE, "chs"); 

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    char szStr[100];

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //注意下面是大寫,在ansi中,代表後面是unicode字元串

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //sprintf

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    sprintf(szStr, "%S", wszString);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    ::MessageBoxA(GetSafeHwnd(), szStr, szStr, MB_OK);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

}

Unicode轉UTF8

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

void  CConvertDlg::OnBnClickedButtonUnicodeToU8()

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

{

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    // unicode to UTF8

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    wchar_t* wszString = L"abcd1234你我他";

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //預轉換,得到所需空間的大小,這次用的函數和上面名字相反

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    int u8Len = ::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //同上,配置設定空間要給'/0'留個空間

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //UTF8雖然是Unicode的壓縮形式,但也是多位元組字元串,是以可以以char的形式儲存

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    char* szU8 = new char[u8Len + 1];

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //轉換

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //unicode版對應的strlen是wcslen

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    ::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), szU8, u8Len, NULL, NULL);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //最後加上'/0'

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    szU8[u8Len] = '/0';

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //MessageBox不支援UTF8,是以隻能寫檔案

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //接下來寫入文本

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //寫文本檔案,UTF8的BOM是0xbfbbef

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    CFile cFile;

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Open(_T("1.txt"), CFile::modeWrite | CFile::modeCreate);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //檔案開頭

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.SeekToBegin();

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //寫BOM,同樣低位寫在前

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Write("/xef/xbb/xbf", 3);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //寫入内容

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Write(szU8, u8Len * sizeof(char));

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Flush();

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    cFile.Close();

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    delete[] szU8;

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    szU8 =NULL;

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

}

UTF8轉UNICODE

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

void  CConvertDlg::OnBnClickedButtonU8ToUnicode()

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

{

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //UTF8 to Unicode

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //由于中文直接複制過來會成亂碼,編譯器有時會報錯,故采用16進制形式

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    char* szU8 = "abcd1234/xe4/xbd/xa0/xe6/x88/x91/xe4/xbb/x96/x00";

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //預轉換,得到所需空間的大小

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //配置設定空間要給'/0'留個空間,MultiByteToWideChar不會給'/0'空間

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    wchar_t* wszString = new wchar_t[wcsLen + 1];

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //轉換

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //最後加上'/0'

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    wszString[wcsLen] = '/0';

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //unicode版的MessageBox API

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    ::MessageBoxW(GetSafeHwnd(), wszString, wszString, MB_OK);

Ansi、Unicode、UTF8字元串之間的轉換,wprintf
Ansi、Unicode、UTF8字元串之間的轉換,wprintf

    //寫文本同ansi to unicode

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

}

Ansi、Unicode、UTF8字元串之間的轉換,wprintf

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的話,就能夠正确顯示了