Windows编程 Windows下Unicode编码

我们知道在ascii编码,每个字符占用一个字节,这样能够表示的字符数远远不够表示世界所有语言的符号,所以Unicode编码就是出现了,当然Unicode也有8位 16位 32位的编码,UTF-8、UTF-16、UTF-32分别以char、char16_t、char32_t作为编码单位,本文讨论16位 即UTF-16,(注: char16_t 和 char32_t 是 C++ 11 标准新增的关键字。如果你的编译器不支持 C++ 11 标准,请改用 unsigned short 和 unsigned long。)

 

当然本文着重讨论Unicode在Windows中的应用,至于具体细节以及原理上的东西,本人才疏学浅,不配在此谈论此问题,呵呵。还请各位到Google去寻找大牛吧。。由于个人能力有限,文中难免有错误之处,还请各位多多批评和指正,多多包涵才是。

许久以前我在学习MFC的过程中,用GetDlgItemText获取Edit Control的值,其得到的值是CString,我们看下该函数的原型:

int GetDlgItemText( HWND hDlg , int nID, LPTSTR lpStr, int nMaxCount) const;
int GetDlgItemText( int nID, CString& rString) const;

CString编码方式为TCHAR,即在定义了UNICODE和_UNICODE(前者是Windows风格后者是C语言风格,效果形同,通常同时定义)时,是wchar_t型,没有定义时是char型。那么在项目设置为多字节编码时,很好处理,比如在网络编程过程中

sendto(
    _In_ SOCKET s,
    _In_reads_bytes_(len) const char FAR * buf,
    _In_ int len,
    _In_ int flags,
    _In_reads_bytes_(tolen) const struct sockaddr FAR * to,
    _In_ int tolen
    );

sendto第二个参数为待发送数据的缓冲区,char类型的指针,这时CString的存储类型就为char ,那么可以直接使用CString的值。

但是当项目的编码方式设置为Unicode时,这时CString的存储类型就为wchar_t,就需要将CString进行类型转换,CString有一个成员函数GetBuffer,这个函数是为一个CString对象重新获取其内部字符缓冲区指针,返回的LPTSTR为非const的,从而允许直接修改CString中的内容。然后就可以强制转换为(char *)类型了。

但是在接收端你必须还要劳烦一次,呵呵  有点啰嗦了,这不是我的风格,代码如下:

wchar_t recvBuf[200];
SOCKADDR_IN addrFrom;
int len=sizeof(SOCKADDR);
recvfrom(sock,(char *)recvBuf,200,0,(SOCKADDR*)&addrFrom,&len);

或者在进行一下处理:

char *ip=inet_ntoa(addrFrom.sin_addr);
wchar_t *wIp=new wchar_t;
memset(wIp,0,sizeof(wchar_t*));
MultiByteToWideChar(CP_ACP,0,ip,-1,wIp,16);      //点分10进制格式地址最长也就15                                                       
wsprintf(tempBuf,L"%s说:%s",wIp,recvBuf);
::PostMessageA(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);

参考:http://blog.163.com/zui_qingbei/blog/static/20957123620126252567489/

关于MFC的CString就先说到这儿吧 !

接下来看下使用

TCHAR

#ifndef _TCHAR_DEFINED
typedef WCHAR TCHAR, *PTCHAR;
typedef WCHAR TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */

#ifndef _MAC
typedef wchar_t WCHAR;    // wc,   16-bit UNICODE character
#else
// some Macintosh compilers don't define wchar_t in a convenient location, or define it as a char
typedef unsigned short WCHAR;    // wc,   16-bit UNICODE character
#endif

可以看出TCHAR 实质就是wchar_t(在Unicode编码下)。

_T("")是一个宏,定义于tchar.h下。

#define _T(x)       __T(x)
#define _TEXT(x)    __T(x)

他的作用是让你的程序支持Unicode编码,因为Windows使用两种字符集ANSI和UNICODE,前者就是通常使用的单字节方式,但这种方式处理象中文这样的双字节字符不方便,容易出现半个汉字的情况。而后者是双字节方式,方便处理双字节字符

Windows NT的所有与字符有关的函数都提供两种方式的版本,而Windows 9x只支持ANSI方式。

如果你编译一个程序为ANSI方式,_T实际不起任何作用。而如果编译一个程序为UNICODE方式,则编译器会把"Hello"字符串以UNICODE方式保存。_T和_L的区别在于,_L不管你是以什么方式编译,一律以UNICODE方式保存。

L是表示字符串资源为Unicode的。比如:

wchar_tStr[] = L"Hello World!";    这个就是双字节存储字符了。

_T是一个适配的宏~ 当#ifdef _UNICODE的时候 ,_T就是L,没有#ifdef _UNICODE的时候,_T就是ANSI的。

LPTSTR lpStr = new TCHAR[32];
TCHAR* szBuf = _T("Hello");

以上两句使得无论是在UNICODE编译条件下还是在ANSI编译条件下都是正确编译的。

_TEXT      //同样定义于tchar.h下

#define _T(x)       __T(x)
#define _TEXT(x)    __T(x)

看出来了吧 _TEXT和_T一样的作用。

TEXT     //定义于winnt.h

#define TEXT(quote) __TEXT(quote)   // r_winnt
#define __TEXT(quote) L##quote      // r_winnt

呵呵 看到这儿,可能有些懵了吧,TEXT   和  _T功能一样?是的,功能一样,但是有了_T为什么还有TEXT呢,我们看他们定义的头文件,TEXT定义于winnt.h,以win开头的是Microsoft的头文件,根据宏内容,winnt.h是以UNICODE定义的,Tchar.h则是以_UNICODE定义的,是有区别的,至于Microsoft为什么这么做,应该是为了windows开发者能够方便统一吧!其实在winnt.h这个头文件中同样对TCHAR之类的类型进行了定义,有兴趣的童鞋可以去看看这两个问价的内容分。

TEXT   和  _T的区别解释的很不准确,欢迎大家指正,待本人理解在深刻一些在做修改吧 !

好吧,要回寝室了,今日就到这儿了,Unicode字符串的处理明日在叙!!!

继续。。。。

说完了使用 接下来说处理吧

先看_tcslen()这个函数 ,在tchar.h 这个头文件里面找到了其定义

#define _tcslen         wcslen

仔细点还可以找到另一个定义

#define _tcslen     strlen

呵呵 ,我想大家已经明白其意思了吧!

在Unicode编码下会使用上面那个宏,当然多字节编码就会使用下面那个了。

的确,Microsoft这样一处理,我们使用起来就方便多了,很容易在项目里面实现统一。

使用和处理都说了 最后说一下转换吧!

有时候项目是采用多字节编码,但是一些windows函数不支持宽字节,比如上面说到了网络编程中的收发数据的函数,这时候就需要我们转换一下:

int WINAPI MultiByteToWideChar(
    _In_ UINT CodePage,
    _In_ DWORD dwFlags,
    _In_NLS_string_(cbMultiByte) LPCCH lpMultiByteStr,
    _In_ int cbMultiByte,
    _Out_writes_to_opt_(cchWideChar, return) LPWSTR lpWideCharStr,
    _In_ int cchWideChar
    );

int WINAPI WideCharToMultiByte(
    _In_ UINT CodePage,
    _In_ DWORD dwFlags,
    _In_NLS_string_(cchWideChar) LPCWCH lpWideCharStr,
    _In_ int cchWideChar,
    _Out_writes_bytes_to_opt_(cbMultiByte, return) LPSTR lpMultiByteStr,
    _In_ int cbMultiByte,
    _In_opt_ LPCCH lpDefaultChar,
    _Out_opt_ LPBOOL lpUsedDefaultChar
    );

这里是我常用的两个函数,当然肯定还有其他方法,这个视编程的情况而定吧。

其他方法请参考:http://www.cnblogs.com/coderlee/archive/2008/01/25/1053311.html

到这里就差不多了吧,最后看几个常用字符串指针

LPSTR:32bit指针指向一个字符串,每个字符占1字节

LPCSTR:32-bit指针指向一个常字符串,每个字符占1字节

LPCTSTR:32-bit指针指向一个常字符串,每字符可能占1字节或2字节,取决于Unicode是否定义

LPTSTR:32-bit指针字符可能占1字节或2字节,取决于Unicode是否定义

都与这里内容较多,各位可以自己把这几个指针类型输入编译器,按F12转到代码,看看这几个类型Windows是如何定义的。

还是简单的说下吧,'L'代表Long,'P'代表Pointer(指针),'STR'表示这个变量是一个字符串,'C'表示Const,'T'表示_T宏,呵呵  说到这儿相信大家以后也能够见名知其意思了吧!

这篇就到底就结束了!希望以后有机会写更精彩的内容呈现给大家。。手好软。。。

posted on 2013-11-14 20:50  ′ Visitors  阅读(858)  评论(0编辑  收藏  举报