1. 程式人生 > >涉及多平臺版本的中英文字元檔案讀寫和轉換

涉及多平臺版本的中英文字元檔案讀寫和轉換


參考文件:

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資料原則上要清掉的。