读书笔记:Windows核心编程 字符和字符串处理

字符编码

一、单字节 ANSI
二、双字节 DBCS
三、windows平台下字符编码
windows Vista使用Unicode-16 双字节编码,支持使用代理扩展至32位4个字节,描述少数使用16位不够的字符。在简化代码与节省空间上做了折衷的处理。
.NET Framework始终使用Unicode-16编码所有字符和字符串。所以在开发Windwos应用程序时,使用unicode-16编码可以改进性能和较少内存消耗。

UTF的其他标准

UTF-8:将某些字符编码为单字节、某些字符编码为双字节、某些字符编码为三字节、某些字符编码为四字节。优点,相当流行,使用广泛。缺点,在对值为0x0800(两字节往上)及以上的大量字符进行编码时,不如UTF-16高效。
UTF-32:将所有字符都编码为4个字节。由于所有编码格式统一,可以写一个算法遍历其中所有的字符而不必考虑其编码格式。使用UTF-32编码不必考虑代理问题,但因为占用内存空间过大,通常不广泛使用,多用于应用程序内部。
UTF将65535个字符划分若干区域,如下图所示:
在这里插入图片描述

ANSI字符和Unicode字符与字符串数据类型

C语言使用8bit表示一个ANSI字符。源码声明一个字符串,编译器会转换为由char(8bit)构成的数组。
Microsoft的C/C++编译器构建了一个内建数据类型wchar_t,表示一个Unicode-16字符。
早期编译器没有提供这个数据类型,所以需要在指定了编译器开关/Zc:wchar_t时,才有该数据类型,默认情况下,此开关为开,也建议打开,这样就可以更好的利用内建类型操纵Unicode-16字符。

声明Unicode字符和字符串的方法

字符
在这里插入图片描述
字符串
在这里插入图片描述
字符串之前的大写L用于向编译器表明编译一个Unicode字符串。
将字符串和字符放入程序数据段后,编译器将会用Unicode-16编码每个字符。

WinNT.h

为了区别于C语言的数据结构,windows的开发团队使用该头文件定义了自己的数据类型,具体如下:
在这里插入图片描述

用于处理字符指针和字符串指针

在这里插入图片描述

Unicode、ANSI预处理宏

在写代码的时候,使用ANSI编码、Unicode编码都可以通过编译,因为winNT.h定义的如下的预处理宏。
在这里插入图片描述

Windows中的Unicode函数和ANSI函数

Windwos的所有版本都使用Unicode编码构建,所有的函数都需要使用Unicode字符串。如果传入ANSI字符串,windows函数首先会自动的转换为Unicode字符串,再传给操作系统处理。
如果应用程序需要ANSI字符串,操作系统会将Unicode字符串转换为ANSI字符串再传递给应用程序。
windows函数的多参数列表中有字符串,通常分为ANSI编码字符串版本和Unicode编码字符串版本。

CreateWindowsEx原型如下

在这里插入图片描述
其中
PCWSTR为Unicode字符编码字符串
PCSTR为ANSI字符编码字符串
同时 函数名后的W表示wide 宽字符
A表示ANSI表示接受ANSI字符串
使用时一般调用CreateWindowEx
因为CreateWindowEx是一个宏
在这里插入图片描述
在预处理阶段就会根据当前是否定义了UNICODE觉得替换哪个版本的CreateWindowEx函数。

CreateWindowEx工作流程

CreateWindowExA的源码是一个转换层,用于分配内存,以便将ANSI字符转换成Unicode字符。之后,代码调用CreateWindowExW,传递Unicode字符。只后,CreateWindowExA释放缓冲区,返回窗口句柄。
所以,在对缓冲区中填充字符串的任何函数,需要将编码格式转换为非Unicode编码,再由系统执行向Unicode编码格式的转换,此步骤耗费内存,执行速度慢。
为了使程序运行高效,避免编码转换带来的开销,一开始便使用Unicode编码开发程序,同时也有助于消除windows的转换函数中的bug。

DLL导入不同编码函数

由DLL提供导出两个编码版本函数,一个ANSI编码函数,另一个Unicode编码函数。
ANSI版本负责分配内存,执行必要的字符串转换,调用Unicode版本函数。

Windows向后兼容

WindowsAPI中的一些函数如WIinExec和OpenFile函数,存在向后兼容16位的Windows程序,其不支持Unicode字符编码格式,在开发过程中应当避免使用。考虑使用新函数CreateProcess和CreateFile函数替代。
在内部,老函数会调用新函数,但由于其不支持Unicode编码,通常其支持的功能略少。

COM组件

Micrsoft将16位的COM移植到了Win32时,规定,所有需要COM接口方法都只接受Unicode字符串。

资源

资源在经过资源编译器编译后,输出文件就是资源的二进制形式,资源中的字符串值都已Unicode字符编码格式存储。

C运行库下的Unicode函数和ANSI函数

与windows处理不同,C运行库下,ANSI和Unicode函数相对独立。ANSI函数不会将字符串转换为Unicode格式。Unicode也不会在内部调用ANSI版本。
在C运行库中,strlen返回ANSI字符串长度的函数。
wcslen函数返回Unicode字符串长度函数。
函数原型定义在String.h中
在这里插入图片描述
同样在预处理阶段根据是否包含_UNICODE预处理命令作了区分。VS在新建C++项目是,就会默认定义_Unicode,先前说过定义Unicode字符编码的好处,不难理解这个默认情况。
windows的团队在其定义函数部分不默认添加UNICODE预处理命令。所以一般项目基于WINDOWS开发的应用,要么同时定义UNICODE(WIN)和_UNICODE(CRT),要么同时未定义。

C运行库中的安全字符串函数

任何的修改字符串的函数都存在一个安全隐患:字符串缓冲区不够大,不能容纳生成的字符串,导致数据被破坏。
C运行库中的Strcpy和wcscpy函数等字符串处理函数并未指定缓冲区的最大长度,其并不知道自己是否会破坏内存,也不会有任何的异常信息。
修改字符串的函数会有安全隐患,strlen、wcslen、_tcslen等不会出现异常。
Microsoft提供一系列新的函数替代原始字符串修改函数,避免安全隐患,其定义在StrSafe.h的头文件中。
新版的字符串处理函数在旧版函数后加_s(代表secure 安全的)
新旧函数原型比较:
在这里插入图片描述
在将一个可写的缓冲区作为参数时,必须提供其大小。
所有后缀为_s函数首要任务是验证参数值,包括指针非NULL、整数范围有效、枚举值有效、缓冲区足够容纳结果数据。
其中任意一项的失败,函数会设置局部于线程的C运行时变量errno,返回errno_t值表示成功或失败。调试版本出错,会跳出DEBUG对话框,发行版出错,程序直接终止。

在处理字符串时如何获得更多控制

为了在执行字符串处理时,提供更多的控制。C运行库新增了一些函数。部分函数原型如下
在这里插入图片描述
所有方法名中都带有Cch,表示Count of characters。字符数
可以用_countof宏来获取此值。
另外一系列名称中含有"Cb"的函数,这些函数要求用字节数指定大小,而不是字符数。使用sizeof获取此值。

函数返回值HRESULT值

在这里插入图片描述
不同于安全函数,这些函数在缓冲区过小时,会发生截断。
判断截断的方法是,检测是否返回了STRSAFE_E_INSUFFICIENT_BUFFER
装入可写缓冲区的那一部分被复制,最后一个可用字符被设为’\0’。

Windows字符串函数

CompareString
在这里插入图片描述
这个函数用于两个字符串进行比较,第一个参数时指定一个区域设置ID,32位值,标识一种语言。CompareString使用这个语言比较两个字符串。同时,我们可以调用windows函数GetThreadLocale来得到主调线程LCID。
在这里插入图片描述
其第二个参数时一组标志,用于修改函数在比较字符串时采用的方法。
其余四个参数指定了两个字符串各自的长度(字符数)。
cch1传入负值,则默认pString1字符串以0结尾,并计算字符串长度。
在这里插入图片描述
CompareStringOrdinal
在这里插入图片描述
CompareStringordinal用于比较程序内部所用的字符串(如路径名、注册表项、XML元素属性等)。
这个函数执行的是码位比较,速度很快。
函数只支持Unicode字符串。

Unicode字符编码优点

1、利于程序本地化
2、可支持所有语言,仅需一个二进制文件(.exe或DLL)。
3、提升了程序运行效率,占用内存更少,与windows内部编码匹配。
4、弃用非Unicode的windows函数,避免BUG。
5、与COM接口标准一致。
6、与.NET Frameworl集成。
7、方便操纵资源。

Unicode与ANSI字符串转换

可以利用MultiByteToWideChar将多字节字符串转换为宽字符字符串。
在这里插入图片描述
uCodePage标识与多字节字符串关联的一个代码页值。
dwFlags运行额外的控制。一般情况不使用,传入0。
pMultiByteStr参数指定要转换的字符串。
cbMulitbyte参数指定字符串的长度(字节数)。
传递-1,函数可以自动判断字符串长度。
函数将转换所得的Unicode版本字符串写入内存缓冲区,内存地址由pWideCharStr参数指定。
cchWideChar参数传入0,函数不会执行转换,而是返回一个宽字符数(包含\0),只有当缓冲区能够容纳该数量的宽字符时,转换才能成功。
多字节转换为Unicode编码的步骤
1、调用MultiBytewideChar
pwideCharStr=NULL,cchWideChar=0,cbMultByte=-1;
2、分配一款足以容纳转换后Unicode字符串的内存,大小为MultiByteToWideChar的调用返回值* sizeof(wchar_t)
3、再次调用MultiByteToWideChar 将缓冲区地址作pWideCharStr参数传入 将上述步骤计算的内存字节量传入cchWideChar
4、使用转换的Unicode字符串
5、使用完毕释放字符串所占内存块。
其中有两次调用,第一次传入函数参数,分配Unicode字符串所需的内存并计算字节数,第二次调用传入地址和得到的内存分配字节使用字符串,使用完毕释放Unicode字符串内存。

WideCharToMultiByte
在这里插入图片描述
用于将款字符转换为多字节字符串
参数基本与MultiByteToWideChar相同
不同是,不需要计算字节数,多了pDefaultChar、pfUsedDefaultChar。在只有一个字符在uCodePage指定的代码页中没有对应表示时,函数才会使用这两个参数。在遇到一个不能转换的wide Char时,pDefayltChar便会指向该字符,如果参数为NULL,系统使用通配符?赋予。
pfUsedDefault参数指向一个bool变量,在至少有一个字符不能转换为对应的多字节形式时,pfUsedDefaultChar设为TRUE。所有都转换成功,设为FALSE。

判断文本是ANSI或者Unicode

IsTextUnicode
在这里插入图片描述
该函数由AdvApi32.dll导出,并在WinBase.h中声明
文本文件的问题在于,内容没有任何硬性的、可供快速判断的规则,所以判断相当困难。
IsTextUnicode函数使用一系列统计性和确定性的方法猜测缓冲区内容。由于此种方法并不精准,函数可能返回错误信息。
参数
pvBuffer 标识缓冲区地址,Void*指针。
cb 指定pvBuffer指向的缓冲区字节数 cb是字节数而不是字符数。IsTextUnicode测试的字节数越多,越精确
pResult 整数地址,函数调用前必须初始化这个整数,用于指出函数执行哪些测试。默认执行全部测试。
返回值
返回 TRUE 认为是Unicode文本
返回 FALSE 认为不是Unicode文本

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值