第二章 字符和字符串处理

1、 几种字符集

尽量使用Unicode来代替ANSI字符串

关于双字节字符集 (double-byte character set, DBCS), 双字节字符集中,一个字符串中每个字符由一个或两个字节组成,处理起来不方便。

关于UTF-8,UTF-8的编码规则是将一些字符编码为1字节,一些为2字节,一些为3字节,一些为4字节;非常流行,但对值为0x0800及以上的大量字符进行编码的时候不如UTF-16方便。

关于UTF-16,在windows VISTA中,每个字符都使用UTF-16来编码, UTF-16为每个字符编码为2个字节,一般情况下unicode指的就是UTF-16编码,.NetFramwork始终使用UTF-16来编码。

关于UTF-32, 为每个字符都使用4字节来编码,比较浪费空间。

结论:以后的程序中都尽量使用UNICODE格式,兼容性和国际化比较方便。

2、 ANSI和UNICODE

ANSI字符即以8位char数据类型表示的字符;

MS C/C++编译器定义了一个内建的数据类型wchar_t,即宽字符,它表示一个UTF-16字符。在visual studio 2005中,项目属性--配置属性—C/C++--语言:【将wchar_t视为内置类型】可以选择。

在MS编译器对wchar_t的支持之前,有一个C头文件定义了一个wchar_t数据类型,如下:

Typedef unsigned short wchar_t;  //无符号短整数

定义unicode变量:wchar_t ch = L’A’; //定义了一个宽字符’A’

关于L, _T, __T, _TEXT, __TEXT, TEXT的区别:

L :      把字符串定义为宽字符串

_T或__T: 如果定义了_UNICODE 则为宽字符,否则不是,tchar.h

_TEXT: 如果定义了_UNICODE则为宽字符,否则不是,tchar.h

__TEXT或TEXT:如果定义了UNICODE宏则为宽字符,否则不是,winnt.h

_UNICODE宏用于C运行期头文件,而UNICODE宏则用于Windows头文件,_UNICODE和UNICODE要么都不定义,要么全都定义。

尽量不要使用L,而是要使用其他的条件宏。

 1 PCHAR == CHAR*
 2
 3 PSTR  == CHAR*
 4
 5 PCSTR == CONST CHAR*
 6
 7 PWCHAR == WCHAR*
 8
 9 PWSTR  == WCHAR*
10
11 PCWSTR == CONST WCHAR*

3、 关于windows函数中的unicode和ansi函数

一个比较常用的函数MessageBox, CWnd类的成员函数,其实是一个宏,定义如下:

1 #ifdef UNICODE
2
3 #define MessageBox MessageBoxW
4
5 #else
6
7 #define MessageBox MessageBoxA
8
9 #endif

在上面两个版本的函数中(W 和 A),如果是A版本,那么函数内部会把ANSI字符(串)参数转换为unicode版本再在内部调用W版本,W版本函数返回时再把相应的输出转换为ANSI格式。 这个过程是需要额外的时间和空间的,因此鼓励使用UNICODE版本。

4、 C运行库中的unicode函数和ansi函数

与windows函数不同的是:C运行库函数的ansi版本不会和windows函数那样在内部转换并调用unicode版本,而是“自力更生”,自己完成函数的执行过程。

在C运行库中,strlen函数返回一个ansi字符串的长度(不包括结尾的’\0’),与之对应的unicode版本的是wcslen,返回unicode字符串的长度(同样不包括’\0’)。这两个函数的原型都在string.h中,为了自动适应哪种类型的字符串还必须包含tchar.h,该头文件定义了如下宏:

1 #ifdef _UNICODE
2
3 #define _tcslen   wcslen
4
5 #else
6
7 #define _tcslen   strlen
8
9 #endif

C运行库的字符串函数:strcpy, wcscpy建议放弃,因为没有指定输入缓冲区的大小。

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

这些函数包含在strsafe.h中,这个头文件包含了string.h,现在每一个不安全的函数都对应一个安全版本,如_tcscpy_s, _tcscat_s,这些函数原型中添加了一个缓冲区大小的参数,字符个数,可以用_countof来获取缓冲区大小(实践证明包含’\0’),_countof在stdlib.h中定义。

安全函数会检查指针不为NULL, 整数在有效范围内,枚举值是有效的,缓冲区足以容纳结果数据等。

C运行时允许我们提供自己的函数来替代C运行时引起的 Debug Assertion Failed(断言失败)。 可见cnblogs中2013.01.21的博客。

除了上面说的新的_s安全字符串函数,C运行库还增加了一些函数,用于在执行字符串处理时提供更多的控制。例如可以控制填充字符、指定如何截断。这些函数有ANSI版本和UNICODE版本。

 1 HRESULT StringCchCat(PTSTR pszDest, size_t cchDest, PCTSTR pszSrc);
 2
 3 HRESULT StringCchCatEx(PTSTR pszDest, size_t cchDest, PCTSTR pszSrc,
 4
 5 PTSTR *ppszDestEnd, size_t *pcchRemaning, DWORD dwFlags);
 6
 7
 8
 9 HRESULT StringCchCopy(PTSTR pszDest, size_t cchDest, PCTSTR pszSrc);
10
11 HRESULT StringCchCopyEx(PTSTR pszDest, size_t cchDest, PCTSTR pszSrc,
12
13 PTSTR *ppszDestEnd, size_t *pcchRemaining, DWORD dwFlags);
14
15
16
17 HRESULT StringCchPrintf(PTSTR pszDest, size_t cchDest, PCTSTR pszFormat, …);
18
19 HRESULT StringCchPrintfEx(PTSTR pszDest, size_t cchDest, PTSTR *ppszDestEnd, size_t *pchRemaining, PCTSTR pszFormat, …);

函数名中的Cch表示字符串,即Count of characters. 另外还有Cb的版本,即count of bytes,字节数。

关于HRESULT返回值,返回S_OK成功,返回STRSAFE_E_INVALID_PARAMETER说明将NULL值传给了一个参数, 返回STRSAFE_E_INSUFFICIENT_BUFFER说明目标缓冲区太小,无法容纳整个源字符串。

扩展版本的参数

Size_t *pcchRemaining: 指向一个变量的指针,返回往缓冲区写完后缓冲区剩余的字符数,包括’\0’。如果pcchRemaing为NULL则不返回计数。

LPTSTR *ppszDestEnd: 如果ppszdDestEnd不为NULL则它将指向终止字符’\0’。

DWROD deFlags:以下一个或多个值

STRSAFE_FILL_BEHIND_NULL:如果函数成功,dwFlags用低字节来填充目标缓冲区的剩余部分(即’\0’之后的部分), 如果用STRSAFE_FILL_BYTE来代替它,那么将用指定的字符来填充目标缓冲区剩余的部分。

STRSAFE_IGNORE_NULLS:把NULL字符串指针视为TEXT(“”);

STRSAFE_FILL_ON_FAILURE:如果函数失败,就用dwFlags的低字节来填充整个目标缓冲区,但目标缓冲区的最后一个字符会被设为’\0’, 如果缓冲区太小从而失败原因是STRSAFE_E_INSUFFICIENT_BUFFER,那么在返回的字符串中,所有的字符都会被替换成填充字符。

STRSAFE_NULL_ON_FAILURE:如果函数失败,就将目标缓冲区的第一个字符设为’\0’,从而是缓冲区字符串成为一个空字符串TEXT(“”),如果失败的原因是STRSAFE_E_INSUFFICIENT_BUFFER,则截断后的字符都会被覆盖。

STRSAFE_NO_TRUNCATION:同STRFAFE_NULL_ON_FAILURE ?

从这几个flags的定义可以看出:其低字节为0x00,即终止字符’\0’。

6、 windows字符串函数

比如lstcat和lstrcpy已经不赞成使用,因为他们无法检测缓冲区益处问题。

与此同时,在shlwapi.h中定义了大量好用的字符串函数,同时还需要shlwapi.lib文件;比如StrForamtKBSize, StrFormatByteSize等。

其实这两个函数都是宏,都有两个W和A版本的函数。

StrForamtKBSize函数的W版本原型如下:

LWSTDAPI_(LPWSTR)   StrFormatKBSizeW(LONGLONG qdw, __out_ecount(cchBuf) LPWSTR pszBuf, UINT cchBuf);

1 //eg.
2
3 LPWSTR pszBuffer = new WCHAR[100];
4
5 memset(pszBuffer, 0, sizeof(WCHAR) * 100);
6
7 LPWSTR pszRet = StrFormatKBSize(12345, pszBuffer, 100);

结果: *pszRet == *pszBuffer == L”13 KB”; psRet指向pszBuffer指向的内存。

另有CompareString(Ex)和CompareStringOrdinal

int CompareString(  //此函数是一个宏 LCID locale,  //区域设置ID,标识一种语言, 可用LCID GetThreadLocale()获得 DWORD dwCmdFlags, //比较的方式 PCTSTR pString1, //字符串1 int cch1,         //字符串1字符数 PCTSTR pString2, //字符串2 int cch2         //字符串2字符数
);

dwCmdFlags:

NORM_IGNORECASE

LINGUISTIC_IGNORECASE : 忽略大小写

NORM_IGNOREKANATYPE:不区分平假名和片假名字符

NORM_IGNORENONSPACE

LINGUISTIC_IGNOREDIACRITIC:忽略non-spacing字符

NORM_IGNORESYMBOLS:忽略符号

NORM_IGNOREWIDTH:不区分同一个字符的单字节和双字节形式

SORT_STRINGSORT:将标点符号当作符号来处理

1 int CompareStringOrdinal(    //只有UNICODE版本
2
3   PCWSTR pString1,  //字符串1
4    int cchCount1,
5   PCWSTR pString2,
6   int cchCount2,
7   BOOL bIgnoreCase
8
9 );

CompareStringOrdinal用于比较程序内部所用的字符串(如路径名、注册表项/值、XML元素/属性等),这个函数不需要LCID,速度快。

上面两个函数的返回值需要注意:返回0说明失败,返回1说明前者小于后者,返回2说明相等,返回3说明前者大于后者。可以把返回值减去2得到结果来与strcmp_s或strcmp的结果保持一致。

7、 推荐的字符串处理方式

使用C运行库的安全版本函数来处理字符串,只要定义了_STDC_WANT_SECURE_LIB符号,这些_s方法都可以用,CrtDefs.h默认定义了此符号,不要取消对此符号的定义。

Unicode和Ansi字符串转换

 1  int MultiByteToWideChar( //返回
 2
 3  UINT uCodePage, //代码页,一般可谓CP_ACP,另有CP_UTF8等
 4
 5  DWORD dwFlags,  //标记,额外的转换控制
 6
 7  PCSTR pMultiByteStr, //源多字节字符串
 8
 9  int cbMultiByte,  //源多字节缓冲区字节数
10
11  PWSTR pWideCharStr, //目的宽字符缓冲区
12
13  int cchWiderChar //目的宽字符缓冲区字符数
14
15 );

一定要注意的是:多字节缓冲区一般用字节表示大小,宽字符缓冲区一般用字符数表示大小。

当为cbMultiByte传入-1时,函数可以自动判断源多字节字符串的长度,当为cchWideChar传入0时,函数不进行转换,而是返回需要的宽字符个数,包括’\0’。

 1 LPCSTR pszSrc = "hello world";
 2
 3 int nRet = MultiByteToWideChar(CP_ACP, 0, pszSrc, -1, NULL, 0);
 4
 5
 6     LPWSTR pszDst = new WCHAR[nRet];
 7
 8     memset(pszDst, 0, nRet * sizeof(WCHAR));
 9
10     nRet = MultiByteToWideChar(CP_ACP, 0, pszSrc, -1, pszDst, nRet);
11
12
13     delete[] pszDst;
14
15     pszDst = NULL;

 1 int WideCharToMultiByte(
 2
 3  UINT uCodePage, //代码页,CP_ACP,另有CP_UTF8
 4
 5  DWORD dwFlags,  //标志,转换控制
 6
 7  PCWSTR pWideCharStr, //源宽字符串
 8
 9  int cchWideChar,  //源宽字符串字符个数
10
11  PSTR pMultiByteStr, //目的多字节缓冲区
12
13  int cbMultiByte, //目的多字节缓冲区的大小
14
15  PCSTR pDefaultChar, //只有一个字符在uCodePage指定的代码页中没有对应的表示时,函数使用pDefaultChar指向的字符
16
17  PBOOL bfUsedDefaultChar //如果至少有一个宽字符不能转换为对应的多字节形式,函数就会把这个变量置为TRUE,否则为FALSE,可用它来验证转换是否成功。
18
19 );

和MultiByteToWideChar类似,如果给cchWideChar传入-1,则函数会自动判断源多字节字符串的字节数,如果给cbMultiByte传入0,则函数会返回需要的字节数,包括’\0’。

 1  LPCWSTR pszWideCharStr = L"hello world";
 2
 3     int nRetValue = WideCharToMultiByte(CP_ACP, 0, pszWideCharStr, -1, NULL, 0, NULL, NULL);  //返回需要的字节数
 5
 6     PSTR pszMultiCharStr = new CHAR[nRetValue / sizeof(WCHAR)]; //这里需要特别注意!
 8
 9     PCSTR pDefaultChar = "C";
10
11     BOOL bUsedDefaultChar = FALSE;
13
14     nRetValue = WideCharToMultiByte(      CP_ACP, 0, pszWideCharStr, -1, pszMultiCharStr, nRetValue, pDefaultChar, &bUsedDefaultChar );
15
16
18     delete[] pszMultiCharStr;
19
20     pszMultiCharStr = NULL;
21
22   if(FALSE == bUsedDefaultChar)
24   {
25
26    //转换成功
28   }
29
30   else
32   {
34     //转换不成功
36   }

8、 判断文本是UNICODE还是ANSI

在AdvApi32.dll中导出,在WinBase.h中声明的:

BOOL IsTextUnicode(CONST PVOID pvBuffer, int cb/*字节数*/, PINT pResult);

INT nResult = 0;BOOL bRet = IsTextUnicode(“hello world”, 12, &nResult);

这个函数测到的结果不一定准确,测试的字节数越多越准确。

转载于:https://www.cnblogs.com/cuish/archive/2013/05/06/3063967.html

【windows核心编程】第二章 字符和字符串处理相关推荐

  1. windows核心编程-第二章 Unicode

    第2章U n i c o d e 随着M i c r o s o f t公司的Wi n d o w s操作系统在全世界日益广泛的流行,对于软件开发人员来说,将目标瞄准国际上的各个不同市场,已经成为一个 ...

  2. windows核心编程-第一章 对程序错误的处理

    第一章-对程序错误的处理 在开始介绍Microsoft Windows 的特性之前,必须首先了解 Wi n d o w s的各个函数是如何进行错误处理的. 当调用一个Wi n d o w s函数时,它 ...

  3. 读书 - Delphi下深入Windows核心编程 第二天

    技术交流,DH讲解. 今天进入这书第二章 钩子(HOOK). 先说说钩子是什么? 钩子其实就在你环境中加上一层过滤,在特殊情况下就触发钩子回调函数. 比如说我们安装了全局的键盘钩子,那么当我们按动键盘 ...

  4. Windows核心编程 第二十章 DLL的高级操作技术

    第2 0章 D L L的高级操作技术 看了下这章的内容,谈不上高级,都是些常用相关,但是还是有一些细节需要注意. 20.1 DLL模块的显式加载和符号链接 如果线程需要调用D L L模块中的函数,那么 ...

  5. Windows核心编程 第九章 线程与内核对象的同步(下)

    9.4 等待定时器内核对象 等待定时器是在某个时间或按规定的间隔时间发出自己的信号通知的内核对象.它们通常用来在某个时间执行某个操作. 若要创建等待定时器,只需要调用C r e a t e Wa i ...

  6. Windows核心编程 第九章 线程与内核对象的同步(上)

    第9章 线程与内核对象的同步 上一章介绍了如何使用允许线程保留在用户方式中的机制来实现线程同步的方法.用户方式同步的优点是它的同步速度非常快.如果强调线程的运行速度,那么首先应该确定用户方式的线程同步 ...

  7. Windows核心编程之核心总结(第一章 错误处理)(2018.5.26)

    前沿 学习Windows核心编程是步入Windows编程殿堂的必经之路,2018年寒假重温了计算机操作系统知识,前阵子又过学习Windows程序设计方面的基础,正所谓打铁要乘热,所以我又入了Windo ...

  8. python核心教程第二版答案_python核心编程第二版第4章习题答案.docx

    python核心编程第二版第4章习题答案.docx 4-1.Python 对象.与所有 Python 对象有关的三个属性是什么?请简单的描述一下. 答案: 所有的 Python 对象都拥有三个特性:身 ...

  9. 《windows核心编程系列》二谈谈ANSI和Unicode字符集

    第二章:字符和字符串处理 使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是 ...

  10. [笔记]Windows核心编程《二十》DLL的高级操作技术

    系列文章目录 [笔记]Windows核心编程<一>错误处理.字符编码 [笔记]Windows核心编程<二>内核对象 [笔记]Windows核心编程<三>进程 [笔记 ...

最新文章

  1. Linux有问必答-如何创建和挂载XFS文件系统
  2. 实现点击在当前位置画一个黑点,打印出当前点击的坐标
  3. JS操作Excel读取和写入(模板操作)
  4. php分享三十三:用php中的register_shutdown_function和fastcgi_finish_request
  5. python ui自动化测试框架_基于python语言下的UI自动化测试框架搭建(一)
  6. oracle number对应java什么类型_JVM系列之数据类型
  7. Node.js介绍及安装
  8. 开源10轴IMU PCB,基于MEMS传感器MPU6500-HMC5983-AK8975-BMP280-MS5611设计,适用于多轴无人机、平衡车、惯导入门
  9. Linux curl 常用示例你都 Get 了吗?| CSDN 博文精选
  10. idea中spark项目Scala语言读取properties文件
  11. 计算机基础应用网络统考题库,2016年9月网络教育《计算机应用基础》统考模拟试题及答案 (1)...
  12. centos7安装(二进制包)mysql8
  13. UE4官网关于GamePlay框架的介绍
  14. 基于PHP MYSQL的高校社团管理系统_高校社团管理系统
  15. 鸟哥的linux私房菜中推荐的linux学习网站
  16. 平面几何常用定理、结论总结 第一篇 三角形及其引线、引圆
  17. CSP 201903-2 二十四点 python (python有如神助)
  18. 气溶胶反演输入转化错误_余涛-气溶胶遥感反演研究20150714.ppt
  19. Python爬取金山词霸每日一句,存储到MySQL中
  20. Python内置函数 max 详解

热门文章

  1. 【汇编语言】开发过程
  2. java.rmi.ConnectException: Connection refused to host:
  3. kafka 查看消费者组
  4. Linux开发_反编译开发_破解简单登录程序外加缓冲区溢出攻击
  5. 用心去体验幸福的感觉
  6. WCF中常用的binding方式
  7. 突然想起今天的博客汇报没写
  8. DHTML【3】--HTML
  9. 解题报告 『占卜DIY(模拟)』
  10. vue开发环境的搭建流程