我们知道在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
到这里就差不多了吧,最后看几个常用字符串指针
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宏,呵呵 说到这儿相信大家以后也能够见名知其意思了吧!
这篇就到底就结束了!希望以后有机会写更精彩的内容呈现给大家。。手好软。。。