UNICODE 环境设置
在 安装 Visual Studio 时,在选择 VC++ 时需要加 入 unicode 选 项,保证相关的库文件可以拷贝到 system32 下。
UNICODE 编译设置:
C/C++, Preprocessor difinitions 去除 _MBCS ,加 _UNICODE,UNICODE
在 ProjectSetting/link/output 中设置 Entry 为 wWinMainCRTStartup
反 之为 MBCS ( ANSI )编译。
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 公司将 COM 从 16 位 Windows 转换成 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 数据类型为 CHAR , LPSTR 和 LPCSTR 。
ANSI/Unicode 通用数据类型为 TCHAR , PTSTR,LPCTSTR 。
6. 如何对 Unicode 进行操作?
字符集 特性 实例
ANSI 操作函数以 str 开头 strcpy
Unicode 操作函数以 wcs 开头 wcscpy
MBCS 操作函数以 _mbs 开头 _mbscpy
ANSI/Unicode 操作函数以 _tcs 开头 _tcscpy ( C 运行期库)
ANSI/Unicode 操作函数以 lstr 开头 lstrcpy ( Windows 函数)
所有新的和未过时的函数在 Windows2000 中都同时拥有 ANSI 和 Unicode 两个版本。 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 。
如: StrCat , StrChr , StrCmp 和 StrCpy 等。
9. 如何编写符合 ANSI 和 Unicode 的应用程序?
( 1 ) 将文本串视为字符数组,而不是 chars 数组或字节数组。
( 2 ) 将通用数据类型(如 TCHAR 和 PTSTR )用于文本字符和字 符串。
( 3 ) 将显式数据类型(如 BYTE 和 PBYTE )用于字节、字节指 针和数据缓存。
( 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 ?
判断如果文本文件的开头两个字 节是 0xFF 和 0xFE ,那幺就是 Unicode ,否则是 ANSI 。
12. 如何判断一段字符串是 ANSI 还是 Unicode ?
用 IsTextUnicode 进行判断。 IsTextUnicode 使 用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此 IsTextUnicode 有可能返回不正确的结果。
13. 如何在 Unicode 与 ANSI 之间转换字符串?
Windows 函数 MultiByteToWideChar 用于将多字节字符串转换成宽字符串;函数 WideCharToMultiByte 将宽字符串转换成等价的多字节字符串。
14. Unicode 和 DBCS 之间的区别
Unicode 使用(特别在 C 程序设计语言环境里)“宽字符集”。「 Unicode 中的每个字符都是 16 位宽而 不是 8 位宽。」在 Unicode 中,没有单单 使用 8 位数值的意义存在。相比之下,在“双位组字符集”中我们仍然处理 8 位数值。有些位组自身定义字符,而某些位组则显示需要和另一个位组共同定义一个字符。
处理 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 ,
不过 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 的思想就是想把所有的字符 统一编码 , 实现一个统一的标准 .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 。
对于已有的 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 环境下字符串做 不同处理的,那么需要参考以上 9 , 10 两条,以适应不同环境得字符串处理要求。
对文件读写也一样。只要调用相 关接口函数时,参数中的字符串前都加上 _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 来开发。