Windows环境下Unicode编程总结

 

转自:http://blog.163.com/lyzaily@126/blog/static/424388372009106112038559/

 

Windows 环境下Unicode 编程总结

UNICODE 环境设置
在安装Visual Studio 时,在选择VC++ 时需要加入unicode 选项,保证相关的库文件可以拷贝到system32 下。

UNICODE
编译设置:
C/C++, Preprocessor difinitions
去除_MBCS ,加_UNICODE,UNICODE
ProjectSetting/link/output 中设置EntrywWinMainCRTStartup
反之为MBCSANSI )编译。

Unicode
:宽字节字符集

1.
如何取得一个既包含单字节字符又包含双字节字符的字符串的字符个数?
可以调用Microsoft Visual C++ 的运行期库包含函数_mbslen 来操作多字节(既包括单字节也包括双字节)字符串。
调用strlen 函数,无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0 之前有多少个字节。

2.
如何对DBCS (双字节字符集)字符串进行操作?
函数 描述
PTSTR CharNext
LPCTSTR ; 返回字符串中下一个字符的地址
PTSTR CharPrev
LPCTSTR, LPCTSTR ); 返回字符串中上一个字符的地址
BOOL IsDBCSLeadByte( BYTE )
; 如果该字节是DBCS 字符的第一个字节,则返回非0

3.
为什么要使用Unicode
1 ) 可以很容易地在不同语言之间进行数据交换。
2 ) 使你能够分配支持所有语言的单个二进制.exe 文件或DLL 文件。
3 ) 提高应用程序的运行效率。
Windows 2000
是使用Unicode 从头进行开发的 , 如果调用任何一个Windows 函数并给它传递一个ANSI 字符串,那幺系统首先要将字符串转换成Unicode ,然后将Unicode 字符串传递给操作 系统。如果希望函数返回ANSI 字符串,系统就会首先将Unicode 字符串转换成ANSI 字符串,然后将结果返回给你的应用程序。进行这些字符串的转换需要占用系统的时间和内存。通过从头开始用Unicode 来开发应用程序,就能够使你的应用程序更加有效地运行。
Windows CE
本身就是使用Unicode 的一种操作系统,完全不支持ANSI Windows 函数
Windows 98
只支持ANSI ,只能为ANSI 开发应用程序。

Microsoft
公司将COM16Windows 转换成Win32 时,公司决定需要字符串的所有COM 接口方法都只能接受Unicode 字符串。

4.
如何编写Unicode 源代码?
Microsoft
公司为Unicode 设计了WindowsAPI ,这样,可以尽量减少代码的影响。实际上,可以编写单个源代码文件,以便使用或者不使用Unicode 来 对它进行编译。只需要定义两个宏(UNICODE_UNICODE ),就可以修改然后重新编译该源文件。
_UNICODE
宏用于 C 运行期 头文件,而UNICODE 宏则用于 Windows 头文件。 当编译源代码模块时,通常必须同时定义这两个宏。

5. Windows
定义的Unicode 数据类型有哪些?
数据类型 说明
WCHAR Unicode
字符
PWSTR
指向Unicode 字符串的指针
PCWSTR
指向一个恒定的Unicode 字符串的指针
对应的ANSI 数据类型为CHARLPSTRLPCSTR
ANSI/Unicode
通用数据类型为TCHARPTSTR,LPCTSTR

6.
如何对Unicode 进行操作?
字符集 特性 实例
ANSI
操作函数以str 开头 strcpy
Unicode
操作函数以wcs 开头 wcscpy
MBCS
操作函数以_mbs 开头 _mbscpy
ANSI/Unicode
操作函数以_tcs 开头 _tcscpy C 运行期库
ANSI/Unicode
操作函数以lstr 开头 lstrcpy
Windows 函数
所有新的和未过时的函数在Windows2000 中都同时拥有ANSIUnicode 两个版本。ANSI 版本函数结尾以 A 表示;Unicode 版本函数结尾以 W 表示。 Windows 会如下定义:
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif // !UNICODE

7.
如何表示Unicode 字符串常量?
字符集 实例
ANSI “string”
Unicode L“string”
ANSI/Unicode T(“string”)
_TEXT(“string”)if( szError[0] == _TEXT(‘J’) ){ }

8.
为什么应当尽量使用操作系统函数?
这将有助于稍稍提高应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程Explorer.exe 所使用。由于这些函数使用得很多,因此,在应用程序运行时,它们可能已经被装入RAM
如:StrCatStrChrStrCmpStrCpy 等。

9.
如何编写符合ANSIUnicode 的应用程序?
1 ) 将文本串视为字符数组,而不是chars 数组或字节数组。
2 ) 将通用数据类型(如TCHARPTSTR )用于文本字符和字符串。
3 ) 将显式数据类型(如BYTEPBYTE )用于字节、字节指针和数据缓存。
4 ) 将TEXT 宏用于原义字符和字符串。
5 ) 执行全局性替换(例如用PTSTR 替换PSTR )。
6 ) 修改字符串运算问题。例如函数通常希望在字符中传递一个缓存的大小,而不是字节。这意味着不应该传递sizeof(szBuffer), 而应该传递 (sizeof(szBuffer)/sizeof(TCHAR) 。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那幺请记住要 按字节来分配内存。这就是说,应该调用
malloc(nCharacters *sizeof(TCHAR)),
而不是调用malloc(nCharacters)

10.
如何对字符串进行有选择的比较?
通过调用CompareString 来实现。
标志 含义
NORM_IGNORECASE
忽略字母的大小写
NORM_IGNOREKANATYPE
不区分平假名与片假名字符
NORM_IGNORENONSPACE
忽略无间隔字符
NORM_IGNORESYMBOLS
忽略符号
NORM_IGNOREWIDTH
不区分单字节字符与作为双字节字符的同一个字符
SORT_STRINGSORT
将标点符号作为普通符号来处理

11.
如何判断一个文本文件是ANSI 还是Unicode
判断如果文本文件的开头两个字节是0xFF0xFE ,那幺就是Unicode ,否则是ANSI

12.
如何判断一段字符串是ANSI 还是Unicode
IsTextUnicode 进行判断。IsTextUnicode 使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此 IsTextUnicode 有可能返回不正确的结果。

13.
如何在UnicodeANSI 之间转换字符串?
Windows
函数 MultiByteToWideChar 用于将多字节字符串转换成宽字符串;函数WideCharToMultiByte 将宽字符串转换成等价的多字节字符串。

//========================================================================

//TITLE:

//    MultiByteToWideCharWideCharToMultiByte 用法详解

//AUTHOR:

//    norains

//DATE:

//    第一版:Monday 25-December -2006

//    增补版:Wednesday 27-December -2006

//    修订版:Wednesday 14-March-2007 ( 修正之前的错误例子)

//Environment:

// EVC4.0 + Standard SDK

//========================================================================

1. 使用方法详解

在本文开始之处, 先简要地说一下何为短字符和宽字符.

所谓的短字符, 就是用8bit 来表示的字符, 典型的应用是ASCII. 而宽字符, 顾名思义, 就是用16bit 表示的字符, 典型的有UNICODE. 关于 windows 下的ASCIIUNICODE 的更多信息, 可以参考这两本经典著作:windows 程序设计》,windows 核心编程》. 这两本书关于这两种字符都有比较详细的解说.

宽字符转换为多个短字符是一个难点, 不过我们只要掌握到其中的要领, 便可如鱼得水.

好吧, 那就让我们开始吧.

这个是我们需要转化的多字节字符串:

char sText[20] = {" 多字节字符串!OK!"};

我们需要知道转化后的宽字符需要多少个数组空间. 虽然在这个里程里面, 我们可以直接定义一个 20*2 宽字符的数组, 并且事实上将运行得非常轻松愉快. 但假如多字节字符串更多, 达到上千个乃至上万个, 我们将会发现其中浪费的内存将会越来越多. 所以 以多字节字符的个数的两倍作为宽字符数组下标的声明绝对不是一个好主意.

所幸, 我们能够确知所需要的数组空间.

我们只需要将MultiByteToWideChar() 的第四个形参设为-1, 即可返回所需的短字符数组空间的个数:

DWORD dwNum = MultiByteToWideChar (CP_ACP, 0, sText, -1, NULL, 0);

接下来, 我们只需要分配响应的数组空间:

wchar_t *pwText;

pwText = new wchar_t[dwNum];

if(!pwText)

{

   delete []pwText;

}

接着, 我们就可以着手进行转换了. 在这里以转换成ASCII 码做为例子:

MultiByteToWideChar (CP_ACP, 0, psText, -1, sText, dwSize);

最后, 使用完毕当然要记得释放占用的内存:

delete []psText;

同理, 宽字符转为多字节字符的代码如下:

wchar_t wText[20] = {L" 宽字符转换实例!OK!"};

DWORD dwNum = WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,NULL,0,NULL,FALSE);

char *psText;

psText = new char[dwNum];

if(!psText)

{

   delete []psText;

}

WideCharToMultiByte (CP_OEMCP,NULL,lpcwszStr,-1,psText,dwNum,NULL,FALSE);

delete []psText;

   如果之前我们已经分配好空间, 并且由于字符串较短, 可以不理会浪费的空间, 仅仅只是想简单地将短字符和宽字符相互转换, 那有没有什么简便的方法呢?

   WIN32 API 里没有符合这种要求的函数, 但我们可以自己进行封装:

   

//-------------------------------------------------------------------------------------

//Description:

// This function maps a character string to a wide-character (Unicode) string

//

//Parameters:

// lpcszStr: [in] Pointer to the character string to be converted

// lpwszStr: [out] Pointer to a buffer that receives the translated string.

// dwSize: [in] Size of the buffer

//

//Return Values:

// TRUE: Succeed

// FALSE: Failed

//

//Example:

// MByteToWChar(szA,szW,sizeof(szW)/sizeof(szW[0]));

//---------------------------------------------------------------------------------------

BOOL MByteToWChar(LPCSTR lpcszStr, LPWSTR lpwszStr, DWORD dwSize)

{

    // Get the required size of the buffer that receives the Unicode

    // string.

    DWORD dwMinSize;

    dwMinSize = MultiByteToWideChar (CP_ACP, 0, lpcszStr, -1, NULL, 0);

    if(dwSize < dwMinSize)

    {

     return FALSE;

    }

  

    // Convert headers from ASCII to Unicode.

    MultiByteToWideChar (CP_ACP, 0, lpcszStr, -1, lpwszStr, dwMinSize);

    return TRUE;

}

//-------------------------------------------------------------------------------------

//Description:

// This function maps a wide-character string to a new character string

//

//Parameters:

// lpcwszStr: [in] Pointer to the character string to be converted

// lpszStr: [out] Pointer to a buffer that receives the translated string.

// dwSize: [in] Size of the buffer

//

//Return Values:

// TRUE: Succeed

// FALSE: Failed

//

//Example:

// MByteToWChar(szW,szA,sizeof(szA)/sizeof(szA[0]));

//---------------------------------------------------------------------------------------

BOOL WCharToMByte(LPCWSTR lpcwszStr, LPSTR lpszStr, DWORD dwSize)

{

   DWORD dwMinSize;

   dwMinSize = WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,NULL,0,NULL,FALSE);

   if(dwSize < dwMinSize)

   {

    return FALSE;

   }

   WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,lpszStr,dwSize,NULL,FALSE);

   return TRUE;

}

使用方法也很简单, 示例如下:

wchar_t wText[10] = {L" 函数示例"};

char sText[20]= {0};

WCharToMByte(wText,sText,sizeof(sText)/sizeof(sText[0]));

MByteToWChar(sText,wText,sizeof(wText)/sizeof(wText[0]));

这两个函数的缺点在于无法动态分配内存, 在转换很长的字符串时可能会浪费较多内存空间; 优点是, 在不考虑浪费空间的情况下转换较短字符串非常方便.

2.MultiByteToWideChar() 函数乱码的问题

有的朋友可能已经发现, 在标准的WinCE4.2WinCE5.0 SDK 模拟器下, 这个函数都无法正常工作, 其转换之后的字符全是乱码. 及时更改MultiByteToWideChar() 参数也依然如此.

不过这个不是代码问题, 其结症在于所定制的操作系统. 如果我们定制的操作系统默认语言不是中 文, 也会出现这种情况. 由于标准的SDK 默认语言为英文, 所以肯定会出现这个问题. 而这个问题的解决, 不能在简单地更改控制面板的" 区域选项"" 默认语 言", 而是要在系统定制的时候, 选择默认语言为" 中文".

系统定制时选择默认语言的位置于:

Platform -> Setting... -> locale -> default language , 选择" 中文", 然后编译即可.

14. Unicode
DBCS 之间的区别
Unicode
使用(特别在C 程序设计语言环境里) 宽字符集 。「Unicode 中的每个字符都是16 位宽而不是8 位宽。」在Unicode 中,没有单单使用8 位数值 的意义存在。相比之下,在 双位组字符集 中我们仍然处理8 位数值。有些位组自身定义字符,而某些位组则显示需要和另一个位组共同定义一个字符。
处 理DBCS 字符串非常杂乱,但是处理Unicode 文字则像处理有秩序的文字。您也许会高兴地知道前128Unicode 字符(16 位代码从 0x00000x007F )就是ASCII 字符,而接下来的128Unicode 字符(代码从0x00800x00FF )是ISO 8859-1ASCII 的扩展。Unicode 中不同部分的字符都同样基于现有的标准。这是为了便于转换。希腊字母表使用从0x03700x03FF 的代码,斯拉夫语使用从0x04000x04FF 的代码,美国使用从0x05300x058F 的代码,希伯来语使用从0x05900x05FF 的代 码。中国、日本和韩国的象形文字(总称为CJK )占用了从0x30000x9FFF 的代码。Unicode 的最大好处是这里只有一个字符集,没有一点含 糊。

15.
衍生标准
Unicode
是一个标准。UTF-8 是其概念上的子集,UTF-8 是具体的编码标准。而UNICODE是所有想达到世界统一编码标准的标准。UTF-8 标准就是UnicodeISO10646 )标准的一种变形方式,
UTF
的全称是:Unicode/UCS Transformation Format ,其实有两种UTF ,一种是UTF-8 ,一种是UTF-16
不过UTF-16 使用较少,其对应关系如下:
Unicode 中编码为 0000 - 007F UTF-8 中编码形式为: 0xxxxxxx
Unicode 中编码为 0080 - 07FF UTF-8 中编码形式为: 110xxxxx 10xxxxxx
Unicode 中编码为 0000 - 007F UTF-8 中编码形式为: 1110xxxx 10xxxxxx 10xxxxxx

utf- 8
unicode 的一个新的编码标准, 其实unicode 有过好几个标准. 我们知道一直以来使用的unicode 字符内码都是16, 它实际上还不能把 全世界的所有字符编在一个平面系统, 比如中国的藏文等小语种, 所以utf-8 扩展到了32, 也就是说理论在utf-8 中可容纳二的三十二次方个字符. UNICODE 的思想就是想把所有的字符统一编码, 实现一个统一的标准.big5gb 都是独立的字符集, 这也叫做远东字符集, 把它拿到德文版的 WINDOWS 上可能将会引起字符编码的冲突.... 早期的WINDOWS 默认的字符集是ANSI.notepad 中输入的汉字是本地编码, 但在 NT/2000 内部是可以直接支持UNICODE 的。notepad.exeWIN9598 中都是ANSI 字符,NT 中则是 UNICODE.ANSIUNICODE 可以方便的实现对应映射, 也就是转换 ASCII8 位范围内的字符集,对于范围之外的字符如汉字它是无法表达的。unicode16 位范围内的字符集,对于不同地区的字符分区分 配,unicode 是多个IT 巨头共同制定的字符编码标准。如果在unicode 环境下比如WINDOWS NT 上,一个字符占两字节16 位,而在ANSI 环境下如WINDOWS98 下一个字符占一个字节8.Unicode 字符是16 位宽,最多允许 65,535 字符,数据类型被称为WCHAR
对于已有的ANSI 字符,unicode 简单的将其扩展为16 位:比如ANSI"A"=0x43, 则对应的UNICODE
"A"= 0x0043
ASCII 用七存放128 个字符,ASCII 是一个真正的美国标准, 所以它不能满足其他国家的需要, 例如斯拉夫语的字母和汉字于是出现了Windows ANSI 字符集, 是一种扩展的ASCII,8 位存放字符,128 位仍然存放原来的ASCII,
而高128 位加入了希腊字母等
if def UNICODE
  TCHAR = wchar
else
  TCHAR = char
你需要在Project/Settings/C/C++/Preprocesser definitions 中添加UNICODE_UNICODE
UINCODE,_UNICODE
都要定义。不定义_UNICODE 的话,用SetText(HWND,LPCTSTR), 将被解释为SetTextA(HWND,LPTSTR), 这时 API 将把你给的Unicode 字符串看作ANSI 字符串,显示乱码。因为windows API 是已经编译好存在于dll 中的,由于不管UNICODE 还是ANSI 字符串,都被看作一段buffer,"0B A3 00 35 24 3C 00 00" 如果按ANSI 读,因为ANSI 字串是以''''''''/0'''''''' 结束的,所以只能读到两字节"0B A3 /0" ,如果按UNICODE 读,将完整的读到''''''''/0/0'''''''' 结束。
由于UNICODE 没有额外的指示位,所以系统必须知道你提供的字串是哪种格式。此外, UNICODE 好象是ANSI C++ 规定的 _UNICODE windows SDK 提供的。如果不编写windows 程序,可以只定义UNICODE
开发过程:
围绕着文件读写、字符串处理展开。文件主要有两种:.txt.ini 文件
1.   
unicode 和非unicode 环境下字符串做不同处理的,那么需要参考以上910 两条,以适应不同环境得字符串处理要求。
对文件读写也一样。只要调用相关接口函数时,参数中的字符串前都加上_TEXT 等相关宏。如果写成的那个文件需要是unicode 格式保存的,那么在创建文件时需要加入一个字节头。
CFile file;
    WCHAR szwBuffer[128];
   
    WCHAR *pszUnicode = L"Unicode string/n"; // unicode string
    CHAR *pszAnsi = "Ansi string/n"; // ansi string
    WORD wSignature = 0xFEFF;
   
    file.Open(TEXT("Test.txt"), CFile::modeCreate|CFile::modeWrite);
   
    file.Write(&wSignature, 2);
   
    file.Write(pszUnicode, lstrlenW(pszUnicode) * sizeof(WCHAR));
    // explicitly use lstrlenW function
   
    MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, szwBuffer, 128);
   
    file.Write(szwBuffer, lstrlenW(szwBuffer) * sizeof(WCHAR));
   
file.Close();
//
以上这段代码在unicode 和非unicode 环境下都有效。这里显式的指明用Unicode 来进行操作。
2.   
在非unicode 环境下,缺省调用的都是ANSI 格式的字符串,此时TCHAR 转换为CHAR 类型的,除非显式定义WCHAR 。所以在这个环境下,如果 读取unicode 文件,那么首先需要移动2 个字节,然后读取得字符串需要用MultiByteToWideChar 来转换,转换后字符串信息才代表 unicode 数据。
3.   
unicode 环境下,缺省调用得都是unicode 格式得字符串,也就是宽字符,此时TCHAR 转换为WCHAR ,相关得API 函数也都调用宽字符类 型的函数。此时读取unicode 文件也和上面一样,但是读取得数据是WCHAR 的,如果要转换成ANSI 格式,需要调用 WideCharToMultiByte 。如果读取ANSI 的,则不用移动两个字节,直接读取然后视需要转换即可。

某些语言(如韩语)必 须在unicode 环境下才能显示,这种情况下,在非unicode 环境下开发,就算用字符串函数转换也不能达到显示文字的目的,因为此时调用得API 函 数是用ANSI 的(虽然底层都是用UNICODE 处理但是处理结果是按照程序员调用的API 来显示的)。所以必须用unicode 来开发。

 

【总结】

看了这么多的资料也专研了这么长时间,也应该对自己的认识做个小结了,如果我们是写 windows 程序时,在编写操作字符的程序时,我们可以调用运行时库的函数,运行时库中的函数接口定义都是行业标准的,也就是说如果你使用的都是运行时 库函数则你的程序在windows 上开发的,但是编译后也可以在linux 系统上运行,只要这连个系统的编译器所使用的运行时库中的函数都严格按照国际标 准实现的就ok

你如果在windows 上编程,编译并运行,你当然可以使用windows 系统提供给你的函 数,这些函数得到了windows 系统的支持。当然这样的程序编译后也只能在windows 系统上运行,其他系统上无法正常执行。所有如果你再 windows 上开发程序,而又想在其他系统上运行,这只能使用运行时库函数了。

小结就这么多,如果有错的地方请看官指正,欢迎交流!!!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值