Visual C++ UNICODE 编码释疑
摘自:http://bbs.51cto.com/thread-530298-1.html
(2008-07-07 15:23:29) & V0 A5 v- N# N; N7 W! D
标签:it 分类:技术与心得 , J* v' Z8 r0 N
UNICODE 环境设置:2 w! F4 t7 \: P" ?7 N+ D, e
在安装Visual Studio 时,在选择VC++时需要加入unicode 选项,保证相关的库文件可以拷贝到
system32 下。) V. {8 h, b- [1 A( l y* Q3 `5 ?
UNICODE 编译设置:
C/C++, Preprocessor difinitions 去除_MBCS,加_UNICODE,UNICODE
在ProjectSetting/link/output 中设置Entry 为wWinMainCRTStartup; X& e( m! p0 k' q: _& L) o
反之为MBCS(ANSI)编译。( z* R) d! }* o1 R7 V! A4 r: m
Unicode :宽字节字符集
1. 如何取得一个既包含单字节字符又包含双字节字符的字符串的字符个数?
可以调用Microsoft Visual C++的运行期库包含函数_mbslen 来操作多字节(既包括单字节也包
括双字节)字符串。
调用strlen 函数,无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0 之前有
多少个字节。
2. 如何对DBCS(双字节字符集)字符串进行操作?- n a9 |) s9 `! o* n9 n
函数 描述2 K4 X5 n5 ^9 J- ~+ w3 V8 c
PTSTR CharNext ( LPCTSTR ); 返回字符串中下一个字符的地址7 r+ K8 e, k$ _& r- f% _) J, v( S
PTSTR CharPrev ( LPCTSTR, LPCTSTR );返回字符串中上一个字符的地址1 `4 e. |2 I4 ^: ?( I
BOOL IsDBCSLeadByte( BYTE );如果该字节是DBCS 字符的第一个字节,则返回非0 值
3. 为什幺要使用Unicode?# r/ f- v U& t* x$ P/ e+ _4 g
(1)可以很容易地在不同语言之间进行数据交换。
(2)使你能够分配支持所有语言的单个二进制.exe 文件或DLL 文件。
(3) 提高应用程序的运行效率。
Windows 2000 是使用Unicode 从头进行开发的,如果调用任何一个Windows 函数并给它传递一
个ANSI 字符串,那幺系统首先要将字符串转换成 Unicode,然后将Unicode 字符串传递给操作
系统。如果希望函数返回ANSI 字符串,系统就会首先将Unicode 字符串转换成ANSI 字符串,
然后将结果返回给你的应用程序。进行这些字符串的转换需要占用系统的时间和内存。通过从
头开始用Unicode 来开发应用程序,就能够使你的应用程序更加有效地运行。
Windows CE 本身就是使用Unicode 的一种操作系统,完全不支持ANSI Windows 函数8 M U% w3 `1 r( Y; P

Windows 98 只支持ANSI,只能为ANSI 开发应用程序。
Microsoft 公司将COM 从16 位Windows 转换成Win32 时,公司决定需要字符串的所有COM 接
口方法都只能接受Unicode 字符串。: M2 _5 E. }% e3 \3 t
4. 如何编写Unicode 源代码?
Microsoft 公司为Unicode 设计了WindowsAPI,这样,可以尽量减少代码的影响。实际上,可以
编写单个源代码文件,以便使用或者不使用Unicode 来对它进行编译。只需要定义两个宏
(UNICODE 和_UNICODE),就可以修改然后重新编译该源文件。) e3 b3 V, `' Y6 {; a8 y/ |5 m7 P; z
_UNICODE 宏用于C 运行期头文件,而UNICODE 宏则用于Windows 头文件。当编译源代码模块
时,通常必须同时定义这两个宏。0 a. x2 g, r" A( w2 X# D3 V9 W1 I' a+ @
5. Windows 定义的Unicode 数据类型有哪些?, ~0 {2 d1 @3 G7 ~/ b' G
数据类型 说明
WCHAR Unicode 字符
PWSTR 指向Unicode 字符串的指针. S: B [5 q9 n2 W% a* ]' G
PCWSTR 指向一个恒定的Unicode 字符串的指针" T8 `) K! w0 w# f3 Y
对应的ANSI 数据类型为CHAR,LPSTR 和LPCSTR。 k5 ?2 g9 r5 h! T: ~* N
ANSI/Unicode 通用数据类型为TCHAR,PTSTR,LPCTSTR。) W$ `/ r- L; P8 N' M; M, x
6. 如何对Unicode 进行操作?
字符集 特性 实例+ h8 b" B( s& R# o1 X
ANSI 操作函数以str 开头 strcpy$ r# i! x* ~+ i' c3 G
Unicode 操作函数以wcs 开头 wcscpy3 c* U" R0 [6 |$ _. G
MBCS 操作函数以_mbs 开头 _mbscpy
ANSI/Unicode 操作函数以_tcs 开头 _tcscpy(C 运行期库)
ANSI/Unicode 操作函数以lstr 开头 lstrcpy(Windows 函数)
所有新的和未过时的函数在Windows2000 中都同时拥有ANSI 和Unicode 两个版本。ANSI 版本
函数结尾以A 表示;Unicode 版本函数结尾以W 表示。Windows 会如下定义:* X% m7 o5 Y% m Y
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW# e9 U& F1 O1 M/ t3 K0 l
#else
#define CreateWindowEx CreateWindowExA
#endif // !UNICODE
7. 如何表示Unicode 字符串常量?
字符集 实例6 q1 ?" ?+ H8 _ Q5 V7 g$ A
ANSI “string”
Unicode L“string”4 @, X" @+ B o h4 F

ANSI/Unicode T(“string”)或_TEXT(“string”)if( szError[0] == _TEXT(‘J’) ){ }7 w$ t8 Z& S1 {- m( G* ^3 N. f
8. 为什幺应当尽量使用操作系统函数?
这将有助于稍稍提高应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序比
如操作系统的外壳进程Explorer.exe 所使用。由于这些函数使用得很多,因此,在应用程序运行
时,它们可能已经被装入RAM。
如:StrCat,StrChr,StrCmp 和StrCpy 等。
9. 如何编写符合ANSI 和Unicode 的应用程序?5 k0 Y1 M+ w$ R2 \9 A
(1)将文本串视为字符数组,而不是chars 数组或字节数组。
(2)将通用数据类型(如TCHAR 和PTSTR)用于文本字符和字符串。 x2 A1 m; t. o; ^& c5 C
(3)将显式数据类型(如BYTE 和PBYTE)用于字节、字节指针和数据缓存。. \, s2 z8 V" h7 G+ A
(4) 将TEXT 宏用于原义字符和字符串。. }( o3 h {) t3 ^ t5 Y, \
(5)执行全局性替换(例如用PTSTR 替换PSTR)。$ b: e# v1 Y8 g) j% D2 T3 D$ O7 c
(6)修改字符串运算问题。例如函数通常希望在字符中传递一个缓存的大小,而不是字节。这意
味着不应该传递sizeof(szBuffer),而应该传递(sizeof(szBuffer)/sizeof(TCHAR)。另外,如果需要
为字符串分配一个内存块,并且拥有该字符串中的字符数目,那幺请记住要按字节来分配内
存。这就是说,应该调用+ z; Y3 w! s; N0 ~! F
malloc(nCharacters *sizeof(TCHAR)),而不是调用malloc(nCharacters)。! u7 Y4 s8 D) k" e9 J0 b( {# `
10. 如何对字符串进行有选择的比较?% Z. m+ ~0 y$ g1 W# e
通过调用CompareString 来实现。: `; \1 H) Z8 _ @0 n( b+ q( j4 N
标志 含义, Q: }9 v4 P- a9 g6 I2 N* \4 n
NORM_IGNORECASE 忽略字母的大小写8 _$ e# c. i2 ]% G/ o
NORM_IGNOREKANATYPE 不区分平假名与片假名字符
NORM_IGNORENONSPACE 忽略无间隔字符
NORM_IGNORESYMBOLS 忽略符号
NORM_IGNOREWIDTH 不区分单字节字符与作为双字节字符的同一个字符
SORT_STRINGSORT 将标点符号作为普通符号来处理) p' j$ u6 F, R, c
11. 如何判断一个文本文件是ANSI 还是Unicode?
判断如果文本文件的开头两个字节是0xFF 和0xFE,那幺就是Unicode,否则是ANSI。) z' u+ f7 e! `
12. 如何判断一段字符串是ANSI 还是Unicode?( I: e! V: F' l; ?5 Z
用IsTextUnicode 进行判断。IsTextUnicode 使用一系列统计方法和定性方法,以便猜测缓存的
内容。由于这不是一种确切的科学方法,因此 IsTextUnicode 有可能返回不正确的结果。
13. 如何在Unicode 与ANSI 之间转换字符串?1 U7 `* J2 Y4 t L7 m0 F
Windows 函数MultiByteToWideChar 用于将多字节字符串转换成宽字符串; 函数
WideCharToMultiByte 将宽字符串转换成等价的多字节字符串。

14. Unicode 和DBCS 之间的区别9 U. F2 D1 J( ~$ D6 N
Unicode 使用(特别在C 程序设计语言环境里)“宽字符集”。「Unicode 中的每个字符都是16 位
宽而不是8 位宽。」在Unicode 中,没有单单使用8 位数值的意义存在。相比之下,在“双位组字
符集”中我们仍然处理8 位数值。有些位组自身定义字符,而某些位组则显示需要和另一个位
组共同定义一个字符。$ J8 \8 x$ I# v( x* Y
处理DBCS 字符串非常杂乱,但是处理Unicode 文字则像处理有秩序的文字。您也许会高兴地
知道前128 个Unicode 字符(16 位代码从 0x0000 到0x007F)就是ASCII 字符,而接下来的128
个Unicode 字符(代码从0x0080 到0x00FF)是ISO 8859-1 对ASCII 的扩展。Unicode 中不同部
分的字符都同样基于现有的标准。这是为了便于转换。希腊字母表使用从0x0370 到0x03FF 的
代码,斯拉夫语使用从0x0400 到0x04FF 的代码,美国使用从0x0530 到0x058F 的代码,希伯来
语使用从0x0590 到0x05FF 的代码。中国、日本和韩国的象形文字(总称为CJK)占用了从
0x3000 到0x9FFF 的代码。Unicode 的最大好处是这里只有一个字符集,没有一点含糊。
15.衍生标准
Unicode 是一个标准。UTF-8 是其概念上的子集,UTF-8 是具体的编码标准。而UNICODE是所
有想达到世界统一编码标准的标准。UTF-8 标准就是Unicode(ISO10646)标准的一种变形方
式,
UTF 的全称是:Unicode/UCS Transformation Format,其实有两种UTF,一种是UTF-8,一种是
UTF-16,& ?" M3 I( F' s2 L
不过UTF-16 使用较少,其对应关系如下:, X' K+ L5 L5 m- I, l4 G' }
在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 的思想就是想把所有的字符统一编码,实现一个统一的标准.big5、gb 都是独立
的字符集,这也叫做远东字符集,把它拿到德文版的 WINDOWS 上可能将会引起字符编码的冲
突....早期的WINDOWS 默认的字符集是ANSI.notepad 中输入的汉字是本地编码,但在 NT/2000
内部是可以直接支持UNICODE 的。notepad.exe 在WIN95 和98 中都是ANSI 字符,在NT 中则是
UNICODE.ANSI 和UNICODE 可以方便的实现对应映射,也就是转换 ASCII 是8 位范围内的字符
集,对于范围之外的字符如汉字它是无法表达的。unicode 是16 位范围内的字符集,对于不同
地区的字符分区分配,unicode 是多个IT 巨头共同制定的字符编码标准。如果在unicode 环境下
比如WINDOWS NT 上,一个字符占两字节16 位,而在ANSI 环境下如WINDOWS98 下一个字符

占一个字节8 位.Unicode 字符是16 位宽,最多允许 65,535 字符,数据类型被称为WCHAR。4 f( e" h7 E2 l6 ^
对于已有的ANSI 字符,unicode 简单的将其扩展为16 位:比如ANSI"A"=0x43,则对应的
UNICODE 为
"A"= 0x0043
而ASCII 用七存放128 个字符,ASCII 是一个真正的美国标准,所以它不能满足其他国家的需要,
例如斯拉夫语的字母和汉字于是出现了Windows ANSI 字符集,是一种扩展的ASCII 码,用8 位存
放字符,低128 位仍然存放原来的ASCII 码,( P6 ~, Q5 B# Y6 j+ o
而高128 位加入了希腊字母等
if def UNICODE3 u, C6 \' x/ t0 ^1 Z' n3 P
TCHAR = wchar5 R$ w; |: v4 Y$ A$ p2 W
else
TCHAR = char$ F; a- {( `! n4 }
你需要在Project\Settings\C/C++\Preprocesser definitions 中添加UNICODE 和_UNICODE# L p. b$ j6 ^- K
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。