涉及多平臺版本的中英文字元檔案讀寫和轉換
參考文件:
1. 在多個編譯版本中,中英文在傳遞時,可能採用不同的格式
_UNICODE定義時,中英文字元都按unicode格式儲存(CString使用CStringW-wchar_t型儲存)
MBCS時,英文字元1個位元組,中文字元兩個位元組(CString使用CStringA-char型儲存)
參考下面來自msdn的說明:我們看到CString基於_UNICODE巨集,儲存資料char 或 w_char型別。
CString is based on the TCHAR data type. If the symbol _UNICODE is defined for your program, TCHAR is defined as type wchar_t, a 16-bit character type; otherwise, it is defined as char, the normal 8-bit character type. Under Unicode, then, CString objects are composed of 16-bit characters. Without Unicode, they are composed of 8-bit char type. When not using _UNICODE, CString is enabled for multibyte character sets (MBCS, also known as double-byte character sets, DBCS).
再來看下:MBCS說明
最常見的 MBCS 實現是雙位元組字符集 (DBCS)。一般來說,Visual C++(尤其是 MFC)完全支援 DBCS
在 MBCS 下,字元被編碼為單位元組或雙位元組。在雙位元組字元中,第一個位元組(即前導位元組)表示它和下一個位元組將被解釋為一個字元。第一個位元組來自留作前導位元組的程式碼範圍。哪個範圍的位元組可以用作前導位元組取決於所使用的內碼表。例如,日文內碼表 932 使用 0x81 到 0x9F 範圍內的位元組作為前導位元組,而朝鮮語內碼表 949 則使用其他範圍的位元組。
2. 當我們涉及中英文儲存互動的時候,需要同時支援兩種情況(MBCS/UNICODE)
程式怎麼寫?
配置檔案怎麼寫?
關於我個人當前有這兩種思路
第一種:檔案格式固定
例如:檔案格式我們採用uncode儲存;
讀取時:程式本身在執行的時候,判斷當前是unicode,還是mbcs,採用
sizeof(TCHAR) == 1 則為 mbcs
sizeof(TCHAR) == 2 則為 unicode
當為unicode時,則直接從檔案中讀取字串即可,就能讀到正確的中英文字元,這些讀出的內容可以正確展現到介面中去
當程式以mbcs格式執行時,需要把unicode的字串內容,通過轉換,轉換成mbcs,然後再展現到介面中去
寫入時:我們一定要採用unicode寫入,當系統為mbcs時,我們把資料轉為unicode儲存到wchar_t*的buffer中,然後寫入。
第二種:記錄字串格式到檔案中
例如:程式寫入檔案的時候,判斷當前的環境,如果為mbcs,則記錄1,如果為unicode,則記錄0。
在讀取的時候,先判斷自身的執行環境mbcs/unicode,然後再讀入檔案中的資訊mbcs/unicode;如果兩者一致,則直接讀入即可。
如果兩者不一致,則需要轉換,轉換到對應的執行環境中的格式。
例如:環境為unicode,檔案儲存的是mbcs,則需要把檔案中的字串轉為unicode,然後再展示
環境為mbcs,檔案儲存為unicode,則需要把檔案中的字元床轉為mbcs,然後展示到介面中
3. MBCS/UNICODE轉換的方法
這裡介紹一個MFC中常用的方法
Unicode to ANSI:假如檔案儲存的型別是Unicode,環境的型別是MBCS
CString cstrName; // 環境為Unicode時,CString等同於CStringA,儲存的資料為char型別
BYTE *pBuffer = new BYTE[dwStrNameLength];
binFile.Read(pBuffer, dwStrNameLength); // 從檔案中讀取出Unicode格式來名稱
{
USER_CONVERSION;
cstrName = W2CT(LPCWSTR(pBuffer));
}
delete pBuffer;
ANSI to Unicode:假如檔案儲存的型別是ASNI,環境的型別是Unicode
CString cstrName; // 環境為Unicode時,CString等同於CStringW,儲存的資料為wchar_t型別
BYTE *pBuffer = new BYTE[dwStrNameLength];
binFile.Read(pBuffer, dwStrNameLength); // 從檔案中讀取出來ANSI格式儲存名稱
{
USER_CONVERSION;
cstrName = A2CT(LPCSTR(pBuffer));
}
delete pBuffer;
W2CT,A2CT 參見MSDN中說明:
CSourceType2[C]DestinationType[EX]
where:
-
SourceType and DestinationType are described in the table below.
-
[C] is present when the destination type must be constant.
-
[EX] is present when the initial size of the buffer must be specified as a template argument.
SourceType/DestinationType
Description
A
ANSI character string.
W
Unicode character string.
T
Generic character string (equivalent to W when _UNICODE is defined, equivalent to A otherwise).
OLE
OLE character string (equivalent to W).
4. USER_CONVERSION/W2CT等使用注意事項
USES_CONVERSION的巨集定義參考如下:
int _convert = 0; (_convert);
UINT _acp = ATL::_AtlGetConversionACP() /*CP_THREAD_ACP*/; (_acp);
LPCWSTR _lpw = NULL; (_lpw);
LPCSTR _lpa = NULL; (_lpa)
A2W巨集定義形如:
(_lpa = lpa) == NULL) ? NULL : _convert = (lstrlenA(_lpa)+1);
(INT_MAX/2<_convert)? NULL : ATLA2WHELPER((LPWSTR) alloca(_convert*sizeof(WCHAR)), _lpa, _convert, _acp);
W2A巨集定義形如:
((_lpw = lpw) == NULL) ? NULL : (_convert = (lstrlenW(_lpw)+1),
(_convert>INT_MAX/2) ? NULL : ATLW2AHELPER((LPSTR) alloca(_convert*sizeof(WCHAR)), _lpw, _convert*sizeof(WCHAR), _acp)
其中其記憶體申請使用的是:alloca
alloca的話從stack申請空間,比較方便,不用我們釋放,函式結束,stack地址自然會回退。
不過我們知道stack空間通常是有限制的,例如2M,所以我們使用時,
1. 避免在一個函式中迴圈使用alloca---擔心溢位了
2. 如果確實需要,要麼使用別的,例如MultiByteToWideChar; 也或者這樣把一次的處理放到函式中,然後迴圈再呼叫
CString InnProc(i) { USER_CONVERSION; ... return A2CT(xxx); }
void Proc() {for (...) { xxx = InnProc(i)};
//這樣的話,每次執行完InnProc,stack地址就回歸一次,就不回越積越多的溢位了
3. alloca出的記憶體,要返回時,請copy一下再返回,這個和臨時變數道理一樣. 上面的例子使用的CString把字串copy了一份拿了出來。要不函式退出時,函式產生的stack資料原則上要清掉的。