《Windows 核心编程》第五版 第2章:Unicode

Unicode

字符集

当调用 strlen 函数时,它在以 0 结尾的单字节字符数组中返回字符的数目。问题是,有些文字和书写规则(比如日文中的汉字就是个典型的例子)的字符集中的符号太多了,因此单字节(它提供的符号最多不能超过 256 个)是根本不敷使用的。为此出现了双字节字符集(DBCS),以支持这些文字和书写规则。

单子节与双字节字符集

在这里插入图片描述

Unicode:宽字节字符集

Unicode 用一个 16 位的值来表示每个字符,总共可以得到 65000个字符。它能够对世界各国的书面文字中的所有字符进行编码,远远超过了单字节字符集的 256 个字符的数目。

目前,已经为阿拉伯文、中文拼音、西里尔字母(俄文)、希腊文、西伯莱文、日文、韩文和拉丁文(英文)字母定义了 Unicode 代码点 。这些字符集中还包含了大量的标点符号、数学符号、技术符号、箭头、装饰标志、区分标志和其他许多字符。如果将所有这些字母和符号加在一起,总计约达 35000 个不同的代码点,这样,总计 65 000多个代码点中,大约还有一半可供将来扩充时使用。

这65 536个字符可以分成不同的区域。表 2-2 显示了这样的区域的一部分以及分配给这些区域的字符。
在这里插入图片描述

使用 Unicode

Unicode 功能
  1. 很容易地在不同语言之间进行数据交换
  2. 使你能够分配支持所有语言的单个二进制 .exe 文件或 DLL 文件
  3. 提高应用程序的运行效率
操作系统与 Unicode
操作系统与 Unicode 的关系
Windows 2000使用 Unicode 从头进行开发的,用于创建窗口、显示文本、字符串操作等所有核心函数都需要用到 Unicode 字符串。如果调用任何一个Windows函数并给他传递一个ANSI字符串,那么系统先把字符串转换成 Unicode,然后将 Unicode 字符串传递给操作系统。字符串的转换需要占用系统的时间和内存(所有转换都是在你看不见的情况下发生的)
Windows 98继承了16位Windows操作系统的特性,不是用来处理Unicode的,如果要增加对Unicode的支持,需要非常大的工作量。仅有少量Unicode函数可以在Windows98 中使用,但可能会出现各样的错误。Windows98 像它的前任产品一样,都是用 ANSI 字符串来进行所有的内部操作。
Windows CEWindows CE操作系统是为小型设备开发的,这些设备的内存很小,并且不带磁盘存储器。Windows CE的设备要在世界各地销售,他们希望降低软件开发成本,这样就能更加容易地开发应用程序。为此,Windows CE 本身就是使用 Unicode 的一种操作系统。为了尽可能把 Windows CE 做得小,完全不支持ANSI Windows 函数,整个应用程序都使用 Unicode
需要注意的问题
  • 尽可能使用 Unicode,如果运行 Windows98,只有在必要时 转换到 ANSI
  • Windows 2000 既支持 Unicode,也支持 ANSI,因此可以为任意一种开发应用程序
  • Windows 98 只支持 ANSI,只能为 ANSI 开发应用程序
  • Windows CE 只支持U n i c o d e,只能为 Unicode 开发应用程序
COM 的简单说明
  • COM 通常用于使不同的组件能够互相进行通信,而 Unicode 则是传递字符串的最佳手段
  • 由此可见,在代码中用 Unicode,与系统通信和与 COM 对象通信的操作会很简单高效
  • 但是为 Windows 98 开发程序时使用 COM 会遇到一些问题

C运行期 库对 Unicode 的支持

  • Microsoft 公司为 Unicode 设计了Windows API
  • 标准的 C 头文件 :
    	typedef unsigned short wchar_t;
    
  • 创建一个缓存,最多存放99个字符的 Unicode 字符串和一个结尾为零的字符
    //创建了一个由100个16位值组成的数组
    wchar_t szBuffer[100];
    
  • C 运行期字符串函数,如strcpy、strchr 和 strcat 等,只能对 ANSI 字符串进行操作,不能正确地处理 Unicode 字符串,因此 ANSI C 也有一些标准的字符串函数,有对应等价的 Unicode 函数
    /*
     * 所有的 Unicode 函数均以 wcs 开头,wcs 是宽字符串的英文缩写。
     * 若要调用 Unicode 函数,只需用前缀 wcs 来取代 ANSI 字符串函数的前缀 str 即可
     * ANSI 规定 C 运行期支持 Unicode 字符和字符串,这意味着始终可以调用 C 运行期函数,以便对 U
     */
     
    char *strcat(char *, const char *);
    wchar_t * wcscat(wchar_t *, const wchar_t *);
    
    char * strchr(const char *, int);
    wchar_t * wcschr(const wchar_t *, wchar_t);
    
    int strcmp(const char *, const char *);
    int wcscmp(const wchar_t *, const wchar_t *);
    
    char * strcpy(char *, const char *);
    wchar_t * wcscpy(wchar_t *, const wchar_t *);
    
    size_t strlen(const char *);
    size_t wcslen(const wchar_t *);
    
  • TChar.h 文件,可以帮助创建 ANSIUnicode 通用源代码文件,包含了一些宏定义
    //定义一个 ANSI / Unicode 通用的字符串数组,使用 TCHAR 数据类型
    //如果定义了 _UNICODE TCHAR 可以声明为下面这个形式
    typedef wchar_t TCHAR;
    
    //如果没有定义 _UNICODE, 则声明为
    typedef char TCHAR;
    
    //用改数据类型,可以分配一个字符串:
    TCHAR char TCHAR;
    
    //也可以创建对字符串的指针:
    TCHAR *szError = "Error";
    
    /*******************************************/
    
    //若要生成一个 Unicode 字符,而不是 ANSI 字符,要将代码改写成下面这个样子
    //字符串前面的大写字母L,用于告诉编译器改字符串应该作为 Unicode 字符串来编译
    TCHAR *szError = L"Error";
    
    //完成上一项需要 _TEXT 宏,在文件 TChar.h 中定义。
    //如果定义了 _UNICODE 那么 _TEXT 定义为:
    #define _TEXT(x) L ##  x
    
    //如果没有定义 _UNICODE,则 _TEXT 将定义为
    #define _TEXT(x) x
    
    //使用该宏,可以改写上面这行代码,这样,无论是否定义了 _UNICODE 宏,它都能够正确地进行编译。
    TCHAR *szError = _TEXT("Error");
    
    // _TEXT 宏 也可以用于字符串
    // 例如要检查一个字符串第一个字符是否是 大写字符 J
    if (szError[0] == _TEXT('J')){
    	// First character is a 'J'
    	.
    	.
    	.
    } else {
    	// First character is not a 'J'
    	.
    	.
    	.
    }
    

Windows 定义的 U

在这里插入图片描述

Windows 中的 Unicode 函数和 ANSI 函数

  • 有两个函数称为CreateWindowEx,其中一个CreateWindowEx 函数接受 Unicode 字符串,另一个CreateWindowEx 函数接受 ANSI 字符串

函数原型:

//接受 Unicode 字符串的函数版本,函数明结尾处的大写字母 W 是英文 wide 的缩写,每个 Unicode 字符串的长度是16位,所以通常称为宽字符
HWND WINAPI CreateWindowExW(
	DWORD dwExStyle,
	PCWSTR pClassName,
	PCWSTR pWindowName,
	DWORD dwStyle,
	int X,
	int Y,
	int nWidth,
	int nHeight,
	HWDN hWndParent.
	HMENU hMenu,
	HINSTANCE hInstance,
	PVOID pParam);

//结尾处的大写字母A表示该函数可以接受 ANSI 字符串
HWND WINAPI CreateWindowExA(
	DWORD dwExStyle,
	PCSTR pClassName,
	PCSTR pWindowName,
	DWORD dwStyle,
	int X,
	int Y,
	int nWidth,
	int nHeight,
	HWDN hWndParent.
	HMENU hMenu,
	HINSTANCE hInstance,
	PVOID pParam);

在 WinUser.h 文件中,CreateWindowEx 实际上定义为一个宏:

#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endid // !UNICODE

在这里插入图片描述
在这里插入图片描述

成为符合 ANSI 和 Unicode 的应用程序

着手将你的应用程序转换成符合 Unicode 的应用程序,要遵循的一些基本原则

  • 将文本串视为字符数组,而不是 chars 数组或子节数组
  • 将通用数据类型 (如 TCHAR 和 PTSTR )用于文本字符和字符串
  • 将显示数据类型(如 BYTE和PBYTE)用于子节、字节指针和数据缓存
  • 将TEXT宏用于原义字符和字符串
  • 修改字符串运算问题,例如函数通常希望你在字符中传递一个缓存的大小,而不是字节。
    • 这意味着你不应该传递 sizeof(szBuffer) ,而应该传递(sizeof( szBuffer ) / sizeof(TCHAR)。
    • 另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那么请记住要按字节来分配内存。
    • 应该调用 malloc(nCharacters *sizeof(TCHAR)),而不是调用 malloc( nCharacters )。
    • 在上面所说的所有原则中,这是最难记住的一条原则,如果操作错误,编译器将不发出任何警告。
Windows 字符串函数

在这里插入图片描述

// 对两个Unicode 字符串进行比较
// LCID :用于设定语言ID,是一个32位值,用于标识一种特定的语言
// DWORD : 用于标识一些标记,用来修改该函数比较两个字符串时所用的方法
// 比 C 运行期函数简单地进行数值比较更有意义
int CompareString(
	LCID icid,
	DWORD fdwStyle,
	PCWSTR pString1,
	int cch1,
	PCTSTR pString2,
	int cch2);
	
// 当lstrcmp 函数系列中任何一个函数调用 CompareString 时,该函数便将调用 Windows 的 GetThreadString 函数的结果作为第一个参数来传递:
LCID GetThreadLocale();

函数 CompareString 第二个参数标记和含义
在这里插入图片描述

tolowertoupper 函数无法正确地转换带有重音符号的字符。为了弥补 C运行期库中的这些不足,必须调用下面
这些 Windows 函数,以便转换 Unicode 字符串的大小写字母。这些函数也可以正确地用于 ANSI 字符串

头两个函数:

// 以下两个函数,既可以转换单个字符,也可以转换以0结尾的整个字符串
// 若要转换整个字符串,只需要传递字符串的地址即可
PTSTR CharLower (PTSTR pszString);
PTSTR CharUpper (PTSTR pszString);

// 若要转换单个字符,必须像这样传递各个字符
TCHAR cLowerCaseChar = CharLower((PTSTR) szString[0]);
// 将单个字符转换成一个 PTSTR,便可调用该函数,将一个值传递给它
// 在这个值中,较低的 16 位包含了该字符,较高的 16 位包含0
// 当该函数看到较高位是 0 时,该函数就知道你想要转换单个字符,而不是整个字符串
// 返回的值是个 32 位值,较低的 16 位中是已经转换的字符

  • printf 函数,如果在定义了 _UNICODE 的情况下编译源码,则希望所有字符和字符串参数代表 Unicode 字符和字符串,但是如果在没有定义 _UNICODE 的情况下编译源代码,则希望传递给它的所有字符和字符串都是 ANSI 字符和字符串
资源

在这里插入图片描述

确定文本是 ANSI 文本还是 Unicode 文本
  • Notepad 记事本程序允许既能打开 Unicode 文件也能打开 ANSI 文件,并且可以创建这些文件,也可以用不同的方法来保存这些文件
    在这里插入图片描述
//IsTextUnicode 函数能够帮助区分:
DWORD IsTextUnicode (CONST PVOID pvBuffer, int cb, PINT pResult);

在这里插入图片描述

在Unicode 与 ANSI 之间转换字符串
  • Windows 函数 MultiByteToWideChar 用于将多字节字符串转换成宽字符串

    int MultiByteToWideChar(
    	UINT uCodePage,
    	DWORD dwFlags,
    	PCSTR pMultiByteStr,
    	int cchMultiByte,
    	PWSTR pWideCharStr,
    	int cchWideChar);
    

    函数解释:
    在这里插入图片描述
    在这里插入图片描述
    函数 WideCharToMultiByte 将宽字符串转换成等价的多字节字符串:

    	int WideCharToMultiByte(
    		UINT uCodePage,
    		DWORD dwFlags,
    		PCWSTR pWideCharStr,
    		int cchWideChar,
    		PSTR pMultiByteStr,
    		int cchMultiByte,
    		PCSTR pDefaultChar,
    		PBOOL pfUsedDefaultChar); 
    

在这里插入图片描述

  • 如何使用这些函数,如动态链接库的函数,能转换字符串中的所有字符
    BOOL StringReverseW(PWSTR pWideCharStr){
    	//Get a pointer to the last character in the string.
    	PWSTR pEndOfStr = pWideCharStr + wcslen(pWideCharStr) - 1;
    
    	wchar_t cCharT;
    	//Repeat until we reach the center character in the string.
    	while(pWideCharStr < pEndOfStr){
    		//Save a character in a temporary variable.
    		cCharT = *pWideCharStr;
    		
    		//Put the last character in the first character.
    		*pWideCharStr = *pEndOfStr;
    		
    		//Put the temporary character in the last character.
    		*pEndOfStr = cCharT;
    
    		//Move in one character from the left.
    		pWideCharStr++;
    	
    		//Move in one character from the right.
    		pEndOfStr--;
    	}
    
    	//The string is reversed; return success.
    	return(TRUE);
    }
    
    你可以编写该函数的 ANSI 版本以便该函数根本不执行转换字符串的实际操作。也可以编写该函数的 ANSI 版本,以便该函数它将 ANSI 字符串转换成 Unicode 字符串,将 Unicode 字符串传递给 StringReverseW 函数,然后将转换后的字符串重新转换成 ANSI 字符串
    BOOL StringReverseA(PSTR pMultiByteStr){
    	PWSTR pWideCharStr;
    	int nLenOfWideCharStr;
    	BOOL fOk = FALSE;
    
    	// Calculate the number of characters needed to hold the wide-character version of the string.
    	nLenOfWideCharStr = MultiByteToWideChar(CP_ACP, 0, pMultiByteStr, -1, NULL, 0);
    	
    	//Allocate memory from the process's default heap to accommodate the size of the wide-character string. Don't forget that MultiByteToWideChar returns the number of characters, not the number of bytes, so you must multiply by the size of a wide character.
    	pWideCharStr = HeapAlloc(GetProcessHeap(), 0, nLenOfWideCharStr * sizeof(WCHAR));
    
    	if(pWideCharStr == NULL)
    		return(fOk);
    	
    	//Convert the multibyte string to a wide-character string. MultiByteToWideChar(CP_ACP, 0, pMultiByteStr, -1, pWideCharStr, nLenOfWideCharStr);
    
    	//Call the wide-character version of the function to do the actual work.
    	fOk = StringReverseW(pWideCharStr);
    
    	if (fOk){
    		//Convert the wide-character string back to a multibyte string.
    		WideCharToMultiByte(CP_ACP, 0, pWideCharStr, -1, pMultiByteStr, strlen(pMultiByteStr), NULL, NULL);
    	}
    	
    	//Free the memory containing the wide-character string.
    	HeapFree(GetProcessHeap(), 0, pWideCharStr);
    
    	return(fOk);
    }
    

最后,在用动态链接库分配的头文件中,可以建立两个函数的原型:

	BOOL StringReverseW(PWSTR pWideCharStr);
	BOOL StringReverseA(PSTR pMultiByteStr);

	#ifdef UNICODE
	#define StringReverse StringReverseW
	#else
	#define StringReverse StringReverseA
	#endif //!UNICODE
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值