以前编码问题总能让自己凑或蒙过去,最近要做一个项目服务器端用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*。
一般地,还有下列类型定义:
#ifdef UNICODE
typedef LPWSTR LPTSTR;
typedef LPCWSTR LPCTSTR;
#else
typedef LPSTR LPTSTR;
typedef LPCSTR LPCT