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);
讀本文之後,覺得有用的話可以繼續讀一下: