1. 程式人生 > >CStdioFile的Writestring寫入中文的總結(unicode-ucs2/utf8-無Bom/ansi-gb2312)

CStdioFile的Writestring寫入中文的總結(unicode-ucs2/utf8-無Bom/ansi-gb2312)

參考:

 (注:位元組序為little-endian  更新20130809)

關於CString的字串結束符

使用Writestring的話和TCHAR型別相關性非常強,我們來看看CString:

       1. 先討論結字串的結束符,對於char型的話是        '\0'     (0x00)

                                                         對於wchar_t型的話是 '\0'\0' (0x00 0x00)

        2. 我們知道CString在不同環境編譯出來,可能是char型,或是wchar_t型 (我們使用TCHAR來統一標識)

        從上面兩點我們能看出,CString儲存時,其結束符號的判斷(ReleaseBuffer(), GetLength())我們可以理解為和char或wchar_t一致

如果不考慮結束符號(不使用CString字串方法),我們可以把CString當成我們申請的一段記憶體進行靈活的使用(執行GetBuffer(size)申請的記憶體,而不執行ReleaseBuffer判斷TCHAR字串結束) 。

下面討論的前提TCHAR為wchar_t:

(TCHAR為char時按utf8/ansi儲存是沒有障礙的,儲存unicode會有結束符識別的問題)

 (通常TCAR為wchar_t,像在vc80,vc90,vc100中)  (vc60, vc70中TCHAR通常使用的是char型)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

先寫給出的開發建議,然後再來詳細介紹:

不建議使用(TypeText):使用Writestring寫入時,檔案開啟模式為TypeText時只會寫入TCHAR雙位元組的一個位元組。需要把儲存的資訊,每一個位元組擴充套件一個00位元組(擴充套件僅僅支援中文情況,英文的話,0x0000 會被當成結束符號;中文的話,TCHAR兩個位元組都不為0x00,所以擴充套件也基本可以)。這樣寫入到檔案的內容才會是我們想要的內容。

建議使用(TypeBinary):使用Writestring寫入時,檔案開啟模式為typeBinary時,沒有這個問題,TCHAR的兩個字元都會寫入到檔案中。

不建議使用(ANSI/UFT8-WriteString):同時有中英文,以ANSI/UTF8為檔案格式時,不建議使用WriteString. 因為這兩個格式英文單位元組,漢字雙位元組/三位元組,整體大小可能為Byte的單數倍。Byte單數倍使用CString儲存時,末尾字元只有一個字元,補充了0x00,寫入檔案多一個0x00。

建議使用(Unicode-WriteString):同時有中英文,以ANSI/UTF8為儲存格式時,建議使用Write寫到檔案中。Unicode儲存格式時,中英文均為雙位元組,可以使用typeBinary格式,使用WirteString寫入.

 TypeText開啟時(unicode格式),例如想要寫入 85 51 99 84 E5 53(內蒙古),則要擴充套件成85 00 51 00 99 00 84 00 E5 00 53 00,也或者使用write例如:

(使用Writestring寫入時,檔案開啟模式為TypeText時只會寫入TCHAR雙位元組的一個位元組。)

CStdioFile unicodeFile(_T("unicode.txt"), CFile::modeCreate|CFile::typeText|CFile::modeWrite);

WORD unicodeFlag = 0xFEFF; //檔案採用unicode格式

unicodeFile.Write((void*)&unicodeFlag, sizeof(WORD));

unicodeFile.Write(_T("內蒙古abc"), sizeof(_T("內蒙古abc"));  

(typeText模式時,使用WriteString寫英文+中文這種格式,如“abc內蒙古”會有問題-只寫TCHAR兩個字元的一個,見最上面的描述;使用Write則沒有問題)

可以使用typeBinary來寫(unicode格式),就不用擴充套件了,例如

CStdioFile unicodeFile(_T("unicode.txt"), CFile::modeCreate|CFile::typeBinary|CFile::modeWrite);

WORD unicodeFlag = 0xFEFF; //檔案採用unicode格式

unicodeFile.Write((void*)&unicodeFlag, sizeof(WORD));

unicodeFile.WriteString(_T("內蒙古abc"));

以下部分是使用檔案格式不同時(unicode-ucs2/utf8/ansi)的情況,都是是可以寫入中文成功的:

-----先看不同字符集下的編碼對比;

unicode-ucs2字符集:每個字元/漢字 佔 兩個位元組(2 Bytes)。例項如下:

CString cstrName(_T("內蒙古"));

cstrName儲存的Hex資訊是:85 51 99 84 E5 53

utf8字符集:每個漢字通常佔3個位元組(3 Bytes).,每個字元佔一個位元組(1 Byte)。例項如下:

通常從utf8格式檔案中Read時的得到的字元:

utf8檔案中的漢字“內蒙古”的編碼HEX是:E5 86 85   E8 92 99   E5 8F A4

ANSI時(gb2312):每個字元佔1個位元組,每個漢字佔兩個位元組。例項如下:

char buffer[] = "內蒙古"

buffer儲存的Hex資訊是:C4 DA C3 C9 B9 C5

再回過頭來看這個問題:

如何把 ("abc內蒙古")使用CFile.WriteString寫入到檔案中

方法有多中,基於儲存格式的不同,轉換目標格式的不同,方法也不同:

首先我們明白一點:中文在ANSI/UNICODE/UTF8 情況,編碼正常時(如上邊"內蒙古"在不同格式的Hex資訊),在UE/Notepad++中都是可以展示的

-----轉成ANSI格式顯示// 注意檔案需要為ANSI格式,否則展示會不OK

(typeBinary模式開啟的模式,不需要擴充套件位元組情況)

1. 獲取ANSI格式

char buffer[100] = "abc內蒙古";  // ANSI格式,長度為7位元組為0x61 62 63 C4 DA C3 C9 B9 C5

2. 以ANSI格式轉到TCHAR字串中,並存入到CString(使用強制轉,強制轉的話,需要補充'\0')

//字串的size不為2的整數倍,儲存在CString裡,需要手動多加一個結束符號'\0',而'\0'寫入檔案會造檔案內容有問題(形如:notepad++開啟時是"abc內蒙古NUL", notepad開啟時是多一個空格,"abc內蒙古 ")  CString cstrName;  char *pChar = (char*)(LPCSTR)cstrName.GetBuffer(strlen(buffer) + 3);
 strcpy(pChar, buffer);
 pChar[strlen(buffer) + 1] ='\0';
 pChar[strlen(buffer) + 2] ='\0';
 cstrName.ReleaseBuffer();
 ansiFile.WriteString(cstrName);  // ANSI的size不為2的整數倍,(形如:notepad++開啟時是"abc內蒙古NUL", notepad開啟時是多一個空格,"abc內蒙古 ") //使用WriteString寫入到檔案中是有點問題的,使用write來寫沒有上面這個問題
file.write((void*)buffer, sizeof(buffer));

-----轉為Unicode格式顯示 // 注意檔案需要為unicode格式,檔案前兩個位元組為 FF FE

(typeBinary模式開啟的模式,不需要擴充套件位元組情況)

(typeText模式時,abc+中文這種格式,a需要補TCHAR的空格,儲存到CString中會有問題,單純中文的話還可以擴充套件一下)

1. 首先我們獲取到unicode格式

CString cstrName ( _T("abc內蒙古"));  

2. 確保檔案頭必須為 檔案前兩個位元組為 FF FE (little endian)(標示檔案已unicode解析) 

WORD unicodeFlag = 0xFEFF;

unicodeFile.Write((void*)&unicodeFlag, sizeof(WORD)); 

4. 再寫入字串就OK了(typeBinary模式開啟的模式,不需要擴充套件位元組)

   file.WriteString(cstrName);

-----轉成UTF8格式顯示的時候 //注意檔案為UTF8格式,否則展示會不OK

(typeBinary模式開啟的模式,不需要擴充套件位元組情況)

1. 首先我們獲取到unicode格式

     CString cstrName ( _T("abc內蒙古"));   // ANSI(3+2*2=7Byte)-->Unicode(5*2=10Byte)  // 給CString初始時,會呼叫MultiByteToWideChar

 2. 然後unicode轉utf8格式

   使用(注意第一個引數使用CP_UTF8

   ::WideCharToMultiByte(CP_UTF8, ..

3. 再寫入字串就OK了

(注意:這個和ANSI有一樣的問題,建議使用Write來寫)

   file.WriteString(cstrName);

 附兩個格式轉換的樣例:

ANSI To Unicode

// CP_ACP  ANSI與Unicode之間轉換。
// CP_UTF8 UTF-8與Unicode之間轉換。
// 函式MultiByteToWideChar返回的長度包括了空格的長度
int unicodeLen = ::MultiByteToWideChar(CP_ACP,
	0,
	(char*)(LPCTSTR)cstr,
	-1,
	NULL,
	0);  
wchar_t* pUnicode = (wchar_t*)(LPCTSTR)(cstrOutput.GetBuffer((unicodeLen + 1) * 2));
::MultiByteToWideChar(CP_ACP,
	0,
	(char*)(LPCTSTR)cstr,
	-1,
	pUnicode,
	unicodeLen);
	

UnicodeToANSI 

int  iANSILen = ::WideCharToMultiByte(CP_ACP,
	0,
	(LPCWSTR)(LPCTSTR)cstr,
	-1,
	NULL,
	0,
	NULL,
	0);  
char* pANSI = (char*)(LPCTSTR)(cstrOutout.GetBuffer(iANSILen + 1));
::WideCharToMultiByte(CP_ACP,
	0,
	(LPCWSTR)(LPCTSTR)cstr,
	-1,
	pANSI,
	iANSILen,
	NULL,
	0);

 讀本文之後,覺得有用的話可以繼續讀一下: