以前編碼問題總能讓自己湊或蒙過去,最近要做一個項目服務器端用Python寫,客戶端用c++,工程編譯的字符集使用UNICODE。之間通過socket進行通信,通信過程中編碼轉換問題把我搞得暈頭轉向,逼著我將編碼問題好好研究一番。
首先先談談VC中的編碼問題,首先編碼我們大致可以分為兩類:文件編碼和內存編碼。文件編碼即源代碼文件的編碼,gbk,UTF-8等。內存編碼即源代碼編譯成為二進制文件的時候采用的編碼。
比如,在VC 2008中我們寫下如下的代碼:
char* a = “a中”;
讓我們分別來看一下文件編碼和內存編碼的區別。
用UntraEdit打開該cpp文件,進入十六進制模式:我們可以看到”a中”對應的編碼為:0×61 0xD6 0xD0,0×61是ASCII碼中的a,0xD6 0xD0是GBK里”中”的編碼。說明,在中文windows環境下,VC創建的文件編碼默認是GBK的。那么我們將文件修改為UTF-8的編碼格式,重新用UE以十六進制模式進入。我們看到”a中”的編碼變為了0×61 0xE4 0xB8 0xAD,0×61仍然是a的編碼,而中的編碼變成了,0xE4 0xB8 0xAD。
從上面的試驗我們還可以知道,GBK中中文為2個字節,UTF-8是3個字節。
下面我們研究一下內存編碼,內存編碼在VC中只有3個選項:Not Set,Use Multi-Byte Character Set和Use Unicode Character Set。這個屬性的修改可以在工程的屬性欄中找到:
讓我們繼續觀察上面的例子。來查看一下什么是內存編碼。我們在程序中設置斷點,調試程序來觀察在程序運行過程中,三種工程屬性下,char* a所指向的那篇內存空間的值是什么,是不是如我們所認為的那樣:
工程屬性 | Not Set | Use Multi-Byte Character Set | Use Unicode Character Set |
內存 | 0×61 0xd6 0xd0 | 0×61 0xd6 0xd0 | 0×61 0xd6 0xd0 |
如上表所示,無論工程屬性如何設置,”中”在內存中都是gbk編碼。這樣的程序拿到非中文的操作系統中執行是會出現亂碼的,那么怎么才能將其用UNICODE進行編碼呢?我們需要用到wchar_t這個類型,wchar_t是一種寬字類型,寬字類型的變量使用方法如下wchar_t* a = L”a中”;即在”a中”前面加上L。
現在,調試一下可以看到,a在內存中的值為0×61 0×00 0×2d 0×4e。wchar_t類型的變量會將用兩個字節存儲英文變量,即在原有ASCII的基礎上補上兩個0.而后面的0×2d 0×4e就是”中”的編碼。
這樣,變量a就在多種語言平臺上都可以正確顯示了。那么,工程屬性中的三種字符集選項有什么作用呢?
讓我們引用MSDN中的原文:
Generic-text data type name | SBCS (_UNICODE, _MBCS not defined) | _MBCS defined | _UNICODE defined |
_TCHAR | char | char | wchar_t |
_tfinddata_t | _finddata_t | _finddata_t | _wfinddata_t |
_tfinddata64_t | __finddata64_t | __finddata64_t | __wfinddata64_t |
_tfinddatai64_t | _finddatai64_t | _finddatai64_t | _wfinddatai64_t |
_TINT | int | int | wint_t |
_TSCHAR | signed char | signed char | wchar_t |
_TUCHAR | unsigned char | unsigned char | wchar_t |
_TXCHAR | char | unsigned char | wchar_t |
_T or _TEXT | No effect (removed by PReprocessor) | No effect (removed by preprocessor) | L (converts following character or string to its Unicode counterpart) |
SBCS、_MBCS和_UNICODE是三個宏,會分別在設置了Not Set、Use Multi-Byte Character Set、Use Unicode Character Set的時候定義。為了程序的可移植性,我們通常不會直接使用wchar_t和L,而是使用TCHAR和T()。這樣,在工程屬性設置不同的情況下,TCHAR和T()會被解釋為不同的內容。
同樣,我們以前測試字符串長度的函數strlen也不要直接使用,為了可移植性,我們通常使用_tcslen或者_tcsclen。
TCHAR.H routine | _UNICODE & _MBCS not defined | _MBCS defined | _UNICODE defined |
_tcslen | strlen | strlen | wcslen |
_tcsclen | strlen | _mbslen | wcslen |
_tcsclen_l | strlen_l | _mbslen_l | wcslen_l |
那么現在的問題是,我想要把數據經過網絡傳輸,現在是否可以直接傳輸wchar_t*的變量呢,答案是不行的,因為wchar_t*的字符串的英文字符表示中存在00,中文英文都用兩個byte。這樣既浪費空間又可能會導致網絡的錯誤。因此我們需要先將其轉換成char*的那種類型,即,英文用一個byte,中文用兩個byte。怎么轉換呢?要使用MultiByteToWideChar和WideCharToMultiByte來互相進行轉換。轉換完了之后再通過網絡傳輸。
好了,現在數據到了Python端,想想看,現在Python端收到的是什么編碼?對,中文是gbk的編碼。然后我們為了在python端實現國際化,要將gbk轉化成unicode。方法很簡單,首先先用
s = struct.unpack(’s’,mysocket.recv(2))來接收一個中文字,然后調用s.decode(”gbk”)來將編碼轉化成為unicode編碼。這樣,編碼問題就完全解決了~
VC++.NET中字符串之間的轉換2007-09-28 20:51一、BSTR、LPSTR和LPWSTR 在Visual C++.NET的所有編程方式中,我們常常要用到這樣的一些基本字符串類型,如BSTR、LPSTR和LPWSTR等。之所以出現類似上述的這些數據類型,是因為不同編程語言之間的數據交換以及對ANSI、Unicode和多字節字符集(MBCS)的支持。 那么什么是BSTR、LPSTR以及LPWSTR呢? BSTR(Basic STRing,Basic字符串)是一個OLECHAR*類型的Unicode字符串。它被描述成一個與自動化相兼容的類型。由于操作系統提供相應的 API函數(如SysAllocString)來管理它以及一些默認的調度代碼,因此BSTR實際上就是一個COM字符串,但它卻在自動化技術以外的多種場合下得到廣泛使用。圖1描述了BSTR的結構,其中DWord值是字符串中實際所占用的字節數,且它的值是字符串中Unicode字符的兩倍。 LPSTR和LPWSTR是Win32和VC++所使用的一種字符串數據類型。LPSTR被定義成是一個指向以NULL(‘/0’)結尾的8位ANSI 字符數組指針,而LPWSTR是一個指向以NULL結尾的16位雙字節字符數組指針。在VC++中,還有類似的字符串類型,如LPTSTR、 LPCTSTR等,它們的含義如圖2所示。 例如,LPCTSTR是指“long pointer to a constant generic string”,表示“一個指向一般字符串常量的長指針類型”,與C/C++的const char*相映射,而LPTSTR映射為 char*。 一般地,還有下列類型定義: |
一. VC常用數據類型列表
二. 常用數據類型轉化
2.1數學類型變量與字符串相互轉換
2.2 CString及string,char *與其他數據類型的轉換和操作
●CString,string,char*的綜合比較
●數學類型與CString相互轉化
●CString與char*相互轉換舉例
●CString 與 BSTR 型轉換
●VARIANT 型轉化成 CString 型
2.3 BSTR、_bstr_t與CComBSTR
2.4 VARIANT 、_variant_t 與 COleVariant
附錄CString及字符串轉及操作詳解
參考書籍:CSDN,<<MFC深入淺出(Second Edit)>>
一.VC常用數據類型列表
| Type | Default Size | Description |
基 礎 類 型
全 是 小 寫
| 說明:這些基礎數據類型對于MFC還是API都是被支持的 | ||
boolean | unsigned 8 bit , | 取值TRUE/FALSE | |
byte | unsigned 8 bit, | 整數,輸出按字符輸出 | |
char | unsigned 8 bit, | 字符 | |
double | signed 64 bit | 浮點型 | |
float | signed32 bit | 浮點型 | |
handle_t |
| Primitive handle type | |
hyper | signed 64 bit | 整型 | |
int | signed 32 bit | 整型 | |
long | signed 32 bit | 整型 | |
short | signed 16 bit | 整型 | |
small | signed 8 bit | 整型 | |
void * | 32-bit | 指向未知類型的指針 | |
wchar_t | unsigned 16 bit | 16位字符,比char可容納更多的字符 | |
|
|
| |
Win32 API 常 用 數 據 類 型
全 大 寫 | 說明: 這些Win32API支持的簡單數據類型主要是用來定義函數返回值,消息參數,結構成員。這類數據類型大致可以分為五大類:字符型、布爾型、整型、指針型和句柄型(?). 總共大概有100多種不同的類型, | ||
BOOL/BOOLEAN | 8bit,TRUE/FALSE | 布爾型 | |
BYTE | unsigned 8 bit |
| |
BSTR CComBSTR _bstr_t | 32 bit | BSTR是指向字符串的32位指針 是對BSTR的封裝 是對BSTR的封裝 | |
CHAR | 8 bit | (ANSI)字符類型 | |
COLORREF | 32 bit | RGB顏色值 整型 | |
DWORD | unsigned 32 bit | 整型 | |
FLOAT | float型 | float型 | |
HANDLE |
| Object句柄 | |
HBITMAP |
| bitmap句柄 | |
HBRUSH |
| brush句柄 | |
HCURSOR |
| cursor句柄 | |
HDC |
| 設備上下文句柄 | |
HFILE |
| OpenFile打開的File句柄 | |
HFONT |
| font句柄 | |
HHOOK |
| hook句柄 | |
HKEY |
| 注冊表鍵句柄 | |
HPEN |
| pen句柄 | |
HWND |
| window句柄 | |
INT | ——– | ——– | |
LONG | ——– | ——— | |
LONGLONG |
| 64位帶符號整型 | |
LPARAM | 32 bit | 消息參數 | |
LPBOOL |
| BOOL型指針 | |
LPBYTE |
| BYTE型指針 | |
LPCOLOREF |
| COLORREF型指針 | |
LPCSTR/LPSTR/PCSTR |
| 指向8位(ANSI)字符串類型指針 | |
LPCWSTR/LPWSTR/PCWSTR |
| 指向16位Unicode字符串類型 | |
LPCTSTR/LPTSTR/PCTSTR |
| 指向一8位或16位字符串類型指針 | |
LPVOID |
| 指向一個未指定類型的32位指針 | |
LPDWORD |
| 指向一個DWORD型指針 | |
其他相似類型: LPHANDLE、LPINT、LPLONG、LPWORD、LPRESULT PBOOL、PBOOLEAN、PBYTE、PCHAR、PDWORD、PFLOAT、PHANDLE、PINT、PLONG、PSHORT…… 說明:(1)在16位系統中 LP為16bit,P為8bit,在32位系統中都是32bit(此時等價) (2)LPCSTR等 中的C指Const,T表示TCHAR模式即可以工作在ANSI下也可UNICODE | |||
SHORT | usigned | 整型 | |
其他UCHAR、UINT、ULONG、ULONGLONG、USHORT為無符號相應類型 | |||
TBYTE |
| WCHAR型或者CHAR型 | |
TCHAR |
| ANSI與unicode均可 | |
VARIANT _variant_t COleVariant |
| 一個結構體參考OAIDL.H _variant_t是VARIANT的封裝類 COleVariant也是VARIANT的封裝類 | |
|
|
| |
|
|
| |
WNDPROC |
| 指向一個窗口過程的32位指針 | |
WCHAR |
| 16位Unicode字符型 | |
WORD |
| 16位無符號整型 | |
WPARAM |
| 消息參數 | |
MFC 獨有 數據 類型 | 下面兩個數據類型是微軟基礎類庫中獨有的數據類型 | ||
POSITION | 標記集合中一個元素的位置的值,被MFC中的集合類所使用 | ||
LPCRECT | 指向一個RECT結構體常量(不能修改)的32位指針 | ||
CString | 其實是MFC中的一個類 | ||
|
|
|
說明:
(1)——-表示省略
(2)1Byte=8Bit,
字與機器有關,在8位系統中:字=1字節,16位系統中,1字=2字節,32位中:1字=4字節,
64位中1字=8字節.不要搞混這些概念.
二.常用數據類型轉化及操作
2.1 數學類型變量與字符串相互轉換(這些函數都在STDLIB.H里)
(1)將數學類型轉換為字符串可以用以下一些函數:
舉例: _CRTIMP char * __cdecl _itoa(int, char *, int);//這是一個將數字轉換為一個字符串類型的函數,最后一個int表示轉換的進制
如以下程序:
int iTyep=3;
char *szChar;
itoa(iType,szChar,2);
cout<<szChar;//輸出為1010
類似函數列表:
_CRTIMP char * __cdecl _itoa(int, char *, int);//為了完整性,也列在其中
_CRTIMP char * __cdecl _ultoa(unsigned long, char *, int);
_CRTIMP char * __cdecl _ltoa(long, char *, int);
_CRTIMP char * __cdecl _i64toa(__int64, char *, int);
_CRTIMP char * __cdecl _ui64toa(unsigned __int64, char *, int);
_CRTIMP wchar_t * __cdecl _i64tow(__int64, wchar_t *, int);
_CRTIMP wchar_t * __cdecl _ui64tow(unsigned __int64, wchar_t *, int);
_CRTIMP wchar_t * __cdecl _itow (int, wchar_t *, int);//轉換為長字符串類型
_CRTIMP wchar_t * __cdecl _ltow (long, wchar_t *, int);
_CRTIMP wchar_t * __cdecl _ultow (unsigned long, wchar_t *, int);
還有很多,請自行研究
(2)將字符串類型轉換為數學類型變量可以用以下一些函數:
舉例: _CRTIMP int __cdecl atoi(const char *);//參數一看就很明了
char *szChar=”88”;
int temp(0);
temp=atoi(szChar);
cout<<temp;
類似的函數列表:
_CRTIMP int __cdecl atoi(const char *);
_CRTIMP double __cdecl atof(const char *);
_CRTIMP long __cdecl atol(const char *);
_CRTIMP long double __cdecl _atold(const char *);
_CRTIMP __int64 __cdecl _atoi64(const char *);
_CRTIMP double __cdecl strtod(const char *, char **);//
_CRTIMP long __cdecl strtol(const char *, char **, int);//
_CRTIMP long double __cdecl _strtold(const char *, char **);
_CRTIMP unsigned long __cdecl strtoul(const char *, char **, int);
_CRTIMP double __cdecl wcstod(const wchar_t *, wchar_t **);//長字符串類型轉換為數學類型
_CRTIMP long __cdecl wcstol(const wchar_t *, wchar_t **, int);
_CRTIMP unsigned long __cdecl wcstoul(const wchar_t *, wchar_t **, int);
_CRTIMP int __cdecl _wtoi(const wchar_t *);
_CRTIMP long __cdecl _wtol(const wchar_t *);
_CRTIMP __int64 __cdecl _wtoi64(const wchar_t *);
還有很多,請自行研究
2.2.CString及string,char *與其他數據類型的轉換和操作
(1)CString,string,char*的綜合比較(這部分CSDN上的作者joise的文章
<< CString,string,char*的綜合比較>>寫的很詳細,請大家在仔細閱讀他的文章.
地址: http://blog.csdn.net/joise/
或參考附錄:
(2)轉換:
●數學類型與CString相互轉化
數學類型轉化為CString
可用Format函數,舉例:
CString s;
int i = 64;
s.Format(“%d”, i)
CString轉換為數學類型:舉例CString strValue(“1.234”);
double dblValue;
dblValue = atof((LPCTSTR)strValue);
●CString與char*相互轉換舉例
CString strValue(“Hello”);
char *szValue;
szValue=strValue.GetBuffer(szValue);
也可用(LPSTR)(LPCTSTR)對CString// 進行強制轉換.
szValue=(LPSTR)(LPCTSTR)strValue;
反過來可直接賦值:
char *szChar=NULL;
CString strValue;
szChar=new char[10];
memset(szChar,0,10);
strcpy(szChar,”Hello”);
strValue=szChar;
●CString 與 BSTR 型轉換
CString 型轉化成 BSTR 型
當我們使用 ActiveX 控件編程時,經常需要用到將某個值表示成 BSTR 類型.BSTR 是一種記數字符串,Intel平臺上的寬字符串(Unicode),并且可以包含嵌入的 NULL 字符。
可以調用 CString 對象的 AllocSysString 方法將 CString 轉化成 BSTR:
CString str;
str = …..; // whatever
BSTR bStr = str.AllocSysString();
BSTR型轉換為CString
如果你在 UNICODE 模式下編譯代碼,你可以簡單地寫成:
CString convert(BSTR bStr)
{
if(bStr == NULL)
return CString(_T(“”));
CString s(bStr); // in UNICODE mode
return s;
}
如果是 ANSI 模式
CString convert(BSTR b)
{
CString s;
if(b == NULL)
return s; // empty for NULL BSTR
#ifdef UNICODEs = b;
#elseLPSTR p = s.GetBuffer(SysStringLen(b) + 1);
::WideCharToMultiByte(CP_ACP, // ANSI Code Page
0, // no flags
b, // source widechar string
-1, // assume NUL-terminated
p, // target buffer
SysStringLen(b)+1, // target buffer length
NULL, // use system default char
NULL); // don”t care if default used
s.ReleaseBuffer();
#endifreturn s;
}
●VARIANT 型轉化成 CString 型
VARIANT 類型經常用來給 COM 對象傳遞參數,或者接收從 COM 對象返回的值。你也能自己編寫返回 VARIANT 類型的方法,函數返回什么類型 依賴可能(并且常常)方法的輸入參數(比如,在自動化操作中,依賴與你調用哪個方法。IDispatch::Invoke 可能返回(通過其一個參數)一個 包含有BYTE、WORD、float、double、date、BSTR 等等 VARIANT 類型的結果,(詳見 MSDN 上的 VARIANT 結構的定義)。在下面的例子中,假設 類型是一個BSTR的變體,也就是說在串中的值是通過 bsrtVal 來引用,其優點是在 ANSI 應用中,有一個構造函數會把 LPCWCHAR 引用的值轉換為一個 CString(見 BSTR-to-CString 部分)。在 Unicode 模式中,將成為標準的 CString 構造函數,參見對缺省::WideCharToMultiByte 轉換的告誡,以及你覺得是否可以接受(大多數情況下,你會滿意的)。VARIANT vaData;
vaData = m_com.YourMethodHere();
ASSERT(vaData.vt == VT_BSTR);
CString strData(vaData.bstrVal);
你還可以根據 vt 域的不同來建立更通用的轉換例程。為此你可能會考慮:
CString VariantToString(VARIANT * va)
{
CString s;
switch(va->vt)
{ /* vt */
case VT_BSTR:
return CString(vaData->bstrVal);
case VT_BSTR | VT_BYREF:
return CString(*vaData->pbstrVal);
case VT_I4:
s.Format(_T(“%d”), va->lVal);
return s;
case VT_I4 | VT_BYREF:
s.Format(_T(“%d”), *va->plVal);
case VT_R8:
s.Format(_T(“%f”), va->dblVal);
return s;
… 剩下的類型轉換由讀者自己完成
default:
ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)
return CString(“”);
} /* vt */
}
2.3 BSTR、_bstr_t與CComBSTR
CComBSTR、_bstr_t是對BSTR的封裝,BSTR是指向字符串的32位指針。
char *轉換到BSTR可以這樣:
BSTR b=_com_util::ConvertStringToBSTR(“數據”);///使用前需要加上頭文件comutil.h
反之可以使用char *p=_com_util::ConvertBSTRToString(b);
2.4(引)VARIANT 、_variant_t 與 COleVariant
VARIANT的結構可以參考頭文件VC98/Include/OAIDL.H中關于結構體tagVARIANT的定義。
對于VARIANT變量的賦值:首先給vt成員賦值,指明數據類型,再對聯合結構中相同數據類型的變量賦值,舉個例子:
VARIANT va;
int a=2001;
va.vt=VT_I4;///指明整型數據
va.lVal=a; ///賦值
對于不馬上賦值的VARIANT,最好先用Void VariantInit(VARIANTARG FAR* pvarg);進行初始化,其本質是將vt設置為VT_EMPTY,下表我們列舉vt與常用數據的對應關系:
unsigned char bVal; VT_UI1
short iVal; VT_I2
long lVal; VT_I4
float fltVal; VT_R4
double dblVal; VT_R8
VARIANT_BOOL boolVal; VT_BOOL
SCODE scode; VT_ERROR
CY cyVal; VT_CY
DATE date; VT_DATE
BSTR bstrVal; VT_BSTR
IUnknown FAR* punkVal; VT_UNKNOWN
IDispatch FAR* pdispVal; VT_DISPATCH
SAFEARRAY FAR* parray; VT_ARRAY|*
unsigned char FAR* pbVal; VT_BYREF|VT_UI1
short FAR* piVal; VT_BYREF|VT_I2
long FAR* plVal; VT_BYREF|VT_I4
float FAR* pfltVal; VT_BYREF|VT_R4
double FAR* pdblVal; VT_BYREF|VT_R8
VARIANT_BOOL FAR* pboolVal; VT_BYREF|VT_BOOL
SCODE FAR* pscode; VT_BYREF|VT_ERROR
CY FAR* pcyVal; VT_BYREF|VT_CY
DATE FAR* pdate; VT_BYREF|VT_DATE
BSTR FAR* pbstrVal; VT_BYREF|VT_BSTR
IUnknown FAR* FAR* ppunkVal; VT_BYREF|VT_UNKNOWN
IDispatch FAR* FAR* ppdispVal; VT_BYREF|VT_DISPATCH
SAFEARRAY FAR* FAR* pparray; VT_ARRAY|*
VARIANT FAR* pvarVal; VT_BYREF|VT_VARIANT
void FAR* byref; VT_BYREF
_variant_t是VARIANT的封裝類,其賦值可以使用強制類型轉換,其構造函數會自動處理這些數據類型。
例如:
long l=222;
ing i=100;
_variant_t lVal(l);
lVal = (long)i;
COleVariant的使用與_variant_t的方法基本一樣,請參考如下例子:
COleVariant v3 = “字符串”, v4 = (long)1999;
CString str =(BSTR)v3.pbstrVal;
long i = v4.lVal;
原文:http://blog.csdn.net/lookahead99/article/details/4999189
新聞熱點
疑難解答