赞
踩
以前编码问题总能让自己凑或蒙过去,最近要做一个项目服务器端用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编码。这样,编码问题就完全解决了~
一、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 UNICODE
s = b;
#else
LPSTR 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();
#endif
return 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;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。