Windows核心编程第二章,字符串的表示以及宽窄字符的转换

目录

  • Windows核心编程,字符串的表示以及宽窄字符的转换

    • 1.字符集

      • 1.1.双字节字符集DBCS
      • 1.2 Unicode字符集
      • 1.3 UTF-8编码
      • 1.4 UTF - 32编码.
      • 1.5 Unicode标准包含的对照表.
    • 2.Ansi字符与Unicode字符的字符串数据类型
      • 2.1.Ansi 与 Unicode数据类型
      • 2.2添加的新的数据类型
      • 2.3 TEXT()宏的使用
    • 3.Windows中的Unicode与Ansi函数
      • 3.1 对于Com接口的移植
    • 4.C 运行库中的Unicode跟Ansi函数
      • 4.1.C运行库中的函数
      • 4.2 C库中的安全函数
    • 5.Unicode使用技巧
    • 6.Unicde标准与Ansi之间的转换
      • 6.1.ANSI转Unicode字符的API
      • 6.2.UniCode转ANSI的API

Windows核心编程,字符串的表示以及宽窄字符的转换

1.字符集

1.1.双字节字符集DBCS

何为双字节字符集,在以前我们都是将文本字符串编码为一组以0结尾的单字符.
可以调用strlen进行判断结尾是否是0进而返回字符串的字符个数.
双字节字符集都是由1个或者2个字节组成.日本的汉子就是字符在0x81到0x9f
之间.或者在0xE0 - 0XFC之间,需要检查下一个字节才能判断是一个完整汉字
对于我们来说,一会1个字节,一会两个字节很麻烦,所以除了UNICODE字符集.

1.2 Unicode字符集

Unicode是一项标准,实在1988年由Apple和Xerox建立的.1991是专门成立协会来开发跟推动Unicode标准的.
Unicode每个字符都是使用了UTF-16的编码,Unicode全称(Unicode Transformation Format)(Unicode转换格式) UTF-16编码是将每个字符编码为2个字节.或者是16位.

注意: Unicode是标准. 而 UTF-16才是编码. 注意两者的区别.

1.3 UTF-8编码

除了上边所说的 UTF-16的编码.其实我们也有其余的UTF标准,如UTF - 8
UTF-8是将一些字符编码为1个字节,一些字符编码为两个字节.一些字符编码为3个字节或者4个字节.
了解:
值在0x0080以下的字符,会压缩为1个字节.这符合美国的标准.
值在0x0080 - 0x7FF之间,字符会转换为2个字节.这对欧洲以及东欧非常适合.
值在0x0800以上的字符,都会是3个字节,适合东亚地区的语言
最后代理对被谢伟4个字节, UTF-8是一种相当流向的编码格式.但值在0x800以上,翻译为3个字节的时候,不如UTF-16编码实用.高效.

1.4 UTF - 32编码.

UTF-32编码就是对每一个字节都编程4个字节存储.
如果打算写一个算法,处理字节数不定的字符.就可以实用这种编码.
这种编码用的少.一般都用于应用程序内部,很少用于网络.

1.5 Unicode标准包含的对照表.

2.Ansi字符与Unicode字符的字符串数据类型

2.1.Ansi 与 Unicode数据类型

ANSI:

在C语言中,char是我们常用的数据类型,这个数据类型表示了一个8位的ANSI字符.
如果源代码中,我们声明一个字符串,那么如果是C编译器,则会把字符串中的字符转换为
Char(8位)数据类型构成的一个数组.
如下:

Char c = ‘A’
Char szBuff[] = “ABC”

SzBuff是一个数组,其中 数组的每一项都是一个字符.

UNICODE:
在VC++中,编译器定义了一个自己建立的数据类型. wchar_t,他表示一个16位的Unicode标准的字符. 早期是没有定义的,我们需要在编译器中增加选项 /Zc:wchar_t 才会定义这个数据类型.
如VC++6.0就是需要指定,或者加宏.
UNICODE标准的字符表示方法

Wchar_t  c = L’A’;  宽字符定义的时候需要加L表示这是个宽字符
Wchar_t  wzBuf[] = L”ABC”;定义字符串.

2.2添加的新的数据类型

为了便于程序的编写,windows在WinNt.h中定义了许多新的类型
如下:

typedef wchar_t WCHAR;    // wc,   16-bit UNICODE character
#else
// some Macintosh compilers don't define wchar_t in a convenient location, or define it as a char
typedef unsigned short WCHAR;    // wc,   16-bit UNICODE character
#endif

typedef WCHAR *PWCHAR, *LPWCH, *PWCH;
typedef CONST WCHAR *LPCWCH, *PCWCH;

typedef _Null_terminated_ WCHAR *NWPSTR, *LPWSTR, *PWSTR;
typedef _Null_terminated_ PWSTR *PZPWSTR;
typedef _Null_terminated_ CONST PWSTR *PCZPWSTR;
typedef _Null_terminated_ WCHAR UNALIGNED *LPUWSTR, *PUWSTR;
typedef _Null_terminated_ CONST WCHAR *LPCWSTR, *PCWSTR;
typedef _Null_terminated_ PCWSTR *PZPCWSTR;
typedef _Null_terminated_ CONST PCWSTR *PCZPCWSTR;
typedef _Null_terminated_ CONST WCHAR UNALIGNED *LPCUWSTR, *PCUWSTR;

typedef _NullNull_terminated_ WCHAR *PZZWSTR;
typedef _NullNull_terminated_ CONST WCHAR *PCZZWSTR;
typedef _NullNull_terminated_ WCHAR UNALIGNED *PUZZWSTR;
typedef _NullNull_terminated_ CONST WCHAR UNALIGNED *PCUZZWSTR;

typedef  WCHAR *PNZWCH;
typedef  CONST WCHAR *PCNZWCH;
typedef  WCHAR UNALIGNED *PUNZWCH;
typedef  CONST WCHAR UNALIGNED *PCUNZWCH;

#if _WIN32_WINNT >= 0x0600 || (defined(__cplusplus) && defined(WINDOWS_ENABLE_CPLUSPLUS))

typedef CONST WCHAR *LPCWCHAR, *PCWCHAR;
typedef CONST WCHAR UNALIGNED *LPCUWCHAR, *PCUWCHAR;

_NullNull_terminated 宏的含义
宏是一个头部注解,藐视了一些类型.如何用作函数的参数以及返回值.
我们可以设置代码分析(Code Analysis) 把/analyze开关添加到编译器的命令行中.这样调用函数的话.编译器则会进行检测了.

2.3 TEXT()宏的使用

在Windows中,定义了TEXT()宏,作用就是,我们上面的内建数据类型.有ANSI数据类型,也有UNICODE类型.为了自动使用数据类型,根据编译器当前选择的字符集选项.自动使用数据类型.
如下:

#ifdef  UNICODE                     // 编译器使用ifdef语句自动判断你是否使用的是UNICODE字符集,如果是则下面生效.

#ifndef _TCHAR_DEFINED
typedef WCHAR TCHAR, *PTCHAR;
typedef WCHAR TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */

typedef LPWCH LPTCH, PTCH;
typedef LPCWCH LPCTCH, PCTCH;
typedef LPWSTR PTSTR, LPTSTR;
typedef LPCWSTR PCTSTR, LPCTSTR;
typedef LPUWSTR PUTSTR, LPUTSTR;
typedef LPCUWSTR PCUTSTR, LPCUTSTR;
typedef LPWSTR LP;
typedef PZZWSTR PZZTSTR;
typedef PCZZWSTR PCZZTSTR;
typedef PUZZWSTR PUZZTSTR;
typedef PCUZZWSTR PCUZZTSTR;
typedef PZPWSTR PZPTSTR;
typedef PNZWCH PNZTCH;
typedef PCNZWCH PCNZTCH;
typedef PUNZWCH PUNZTCH;
typedef PCUNZWCH PCUNZTCH;
#define __TEXT(quote) L##quote      // r_winnt

#else   /* UNICODE */               // r_winnt

#ifndef _TCHAR_DEFINED
typedef char TCHAR, *PTCHAR;
typedef unsigned char TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */

typedef LPCH LPTCH, PTCH;
typedef LPCCH LPCTCH, PCTCH;
typedef LPSTR PTSTR, LPTSTR, PUTSTR, LPUTSTR;
typedef LPCSTR PCTSTR, LPCTSTR, PCUTSTR, LPCUTSTR;
typedef PZZSTR PZZTSTR, PUZZTSTR;
typedef PCZZSTR PCZZTSTR, PCUZZTSTR;
typedef PZPSTR PZPTSTR;
typedef PNZCH PNZTCH, PUNZTCH;
typedef PCNZCH PCNZTCH, PCUNZTCH;
#define __TEXT(quote) quote         // r_winnt

#endif /* UNICODE */                // r_winnt
#define TEXT(quote) __TEXT(quote)   // r_winnt

typedef SHORT *PSHORT;
typedef LONG *PLONG;    

3.Windows中的Unicode与Ansi函数

在Windows中,API(应用程序接口) 是分为A版本,与W版,经常听别人怎么说.
其实意思就是你使用的函数兼容Ansi字符的还是兼容Unicode标准的.

在Windows NT版本以后,所有的版本都是用Unicode组建的,核心都是Unicode组建的.
也就是说你调用A版函数.底层会转换参数,供W版使用.
如我们常见的函数:
MessageBoxA MessageBoxW LoadLibraryA LoadLibraryW
有时候我们调用的是Windows的宏, 如 我们直接调用 MessageBox. 其实它是一个宏,跟Text()宏一样,判断你当前的编译器环境,如果是Unicode环境,那么他就会调用 MessageBoxW.

3.1 对于Com接口的移植

Windows逐渐提供W版本函数但是也会有A函数.这种是不能替代的.
而Com组件从16位移植到32位的时候.都是使用的Unicode字符串作为参数了.
因为Com接口适用于让不同的组建进行对话.而Unicode是最好的选择.

4.C 运行库中的Unicode跟Ansi函数

4.1.C运行库中的函数

C运行库中的函数不存在 A W版本.你调用的A就是A,你调用的W就是W
如我们常见的字符串函数 strcmp 与 wcscmp. 必须使用不同的函数才可以.
而这些函数.在Tchar.h中.定义了宏.

#define _tcscat         wcscat
#define _tcscat_s       wcscat_s
#define _tcschr         wcschr
#define _tcscpy         wcscpy
#define _tcscpy_s       wcscpy_s
#define _tcscspn        wcscspn
#define _tcslen         wcslen
#define _tcsnlen        wcsnlen
#define _tcsncat        wcsncat
#define _tcsncat_s      wcsncat_s
#define _tcsncat_l      _wcsncat_l
#define _tcsncat_s_l    _wcsncat_s_l
#define _tcsncpy        wcsncpy
#define _tcsncpy_s      wcsncpy_s
#define _tcsncpy_l      _wcsncpy_l
#define _tcsncpy_s_l    _wcsncpy_s_l
#define _tcspbrk        wcspbrk
#define _tcsrchr        wcsrchr
#define _tcsspn         wcsspn
#define _tcsstr         wcsstr
#define _tcstok         _wcstok
#define _tcstok_s       wcstok_s
#define _tcstok_l       _wcstok_l
#define _tcstok_s_l     _wcstok_s_l
#define _tcserror       _wcserror
#define _tcserror_s     _wcserror_s
#define __tcserror      __wcserror
#define __tcserror_s    __wcserror_s

#define _tcsdup         _wcsdup
#define _tcsnset        _wcsnset
#define _tcsnset_s      _wcsnset_s
#define _tcsnset_l      _wcsnset_l
#define _tcsnset_s_l    _wcsnset_s_l
#define _tcsrev         _wcsrev
#define _tcsset         _wcsset
#define _tcsset_s       _wcsset_s
#define _tcsset_l       _wcsset_l
#define _tcsset_s_l     _wcsset_s_l

#define _tcscmp         wcscmp
#define _tcsicmp        _wcsicmp
#define _tcsicmp_l      _wcsicmp_l
#define _tcsnccmp       wcsncmp
#define _tcsncmp        wcsncmp
#define _tcsncicmp      _wcsnicmp
#define _tcsncicmp_l    _wcsnicmp_l
#define _tcsnicmp       _wcsnicmp
#define _tcsnicmp_l     _wcsnicmp_l

#define _tcscoll        wcscoll
#define _tcscoll_l      _wcscoll_l
#define _tcsicoll       _wcsicoll
#define _tcsicoll_l     _wcsicoll_l
#define _tcsnccoll      _wcsncoll
#define _tcsnccoll_l    _wcsncoll_l
#define _tcsncoll       _wcsncoll
#define _tcsncoll_l     _wcsncoll_l
#define _tcsncicoll     _wcsnicoll
#define _tcsncicoll_l   _wcsnicoll_l
#define _tcsnicoll      _wcsnicoll
#define _tcsnicoll_l    _wcsnicoll_l
使用  _t开头是Windows定义的宏,作用就是,当你定义了Unicode标准的时候,就会扩展为使用Unicode标注你的C库函数.如 _tcslen ,如果是Unicode标注,则使用 wcslen,否则则使用strlen.

4.2 C库中的安全函数

在C库中定义了许多安全函数. 如 strcpy字符串拷贝与wcscpy
它们只是负责拷贝.但是并不会决定拷贝多少.以至于会破坏内存.
破坏内存则会引起 黑客的攻击. 所以现在一般都是 后面加上s

Strcpy_s 等等.可以指定长度.

5.Unicode使用技巧

###5.1字符与字符串的处理方式
作用 演示
TCHAR 或者 PTSTR 来表示文本字符以及字符串

    TCHAR Buffer[] = TEXT(“”);PTSTR Buffer = TEXT(‘a’);

TEXT() _T()宏进行包含 作用就是根据环境自动选择字符集.
_countof()函数 求缓冲区大小,不是使用sizeof(szBuffer)了,使用_countof(szbuff);
如:

Void *p = malloc(_countof(szbuffer));

%s %S %ls格式化输出 %s输出ANSI
%ls输出宽字符
_tcslen_s()安全的函数 多使用后缀带有_s的函数.
/Gs 检测缓冲区溢出 Gs选项可以检测缓冲区溢出
Lstrcat lstrcpy 这个是Kerner32中处理字符的方法,不要使用.
CompareStringOrdinal
CompareStringEx 检测XML元素属性/注册表使用这些进行比较.不用考虑区域设置.

6.Unicde标准与Ansi之间的转换

主要是两个API

6.1.ANSI转Unicode字符的API

int MultiByteToWideChar(UINT CodePage,
 DWORD dwFlags,
 _In_NLS_string_(cbMultiByte)LPCCHlpMultiByteStr, int cbMultiByte,
 LPWSTR lpWideCharStr,
 int cchWideChar );

参数:
Codepage:用于执行转换的代码页,跟国际有关.一般使用 CP_ACP值,作用就是使用当前系统默认的Ansi代码页.

dwFlags: 允许我们进行额外的控制.一般不使用.所以填充0.

lpMultibyteStr: 你要转换的Ansi数组
CbMultibyte: 你要转换的Ansi数组的大小
LpWideCharstr: 传入一个宽字符的空数组.转换后的字符串传出到这个数组中.
Cchwidechar: 你传入的宽字符空数组的大小.

例子:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{

    char szBuffer[] = "HelloWorld";
    DWORD szBufferLen = sizeof(szBuffer) / sizeof(szBuffer[0]);
    TCHAR wszBuffer[100] = { 0 };
    DWORD dwWszBufferLen = 100;

    MultiByteToWideChar(CP_ACP, 0, szBuffer, szBufferLen,(LPWSTR)&wszBuffer, dwWszBufferLen);

    printf("%ls \r\n", wszBuffer);
    system("pause");

    return 0;
}

注意传入宽字符数组的时候,传入的是它的地址.你传入地址函数内部才能根据地址将转换的ANSI转换后的值赋值给字符串数组.

6.2.UniCode转ANSI的API

int
WINAPI
WideCharToMultiByte(
    _In_ UINT CodePage,
    _In_ DWORD dwFlags,
    _In_NLS_string_(cchWideChar) LPCWCH lpWideCharStr,
    _In_ int cchWideChar,
    _Out_writes_bytes_to_opt_(cbMultiByte,return) LPSTR lpMultiByteStr,
    _In_ int cbMultiByte,
    _In_opt_ LPCCH lpDefaultChar,
    _Out_opt_ LPBOOL lpUsedDefaultChar
    );

参数比ANSI转UNicode多了最后两个
LpDefaultChar:这个选项是你转换后你的字符不能正常使用.你则需要指定代码页.
使用系统默认的则使用NULL,. 要获取系统默认字符就要用GetCPinfo或者加Ex的函数.
LpUsedDefaultChar:指向该标志的指针,标志表示该函数是否在转换中使用默认字符.
如果一个字符,是无法在代码页中表示的话,我们设置为TRUE,否则就设置为FALSE或者NULL

例子:

int main()
{

    TCHAR wzBuffer[] = TEXT("HelloWorld");
    char szBuff[100] = { 0 };
    DWORD szBufflen = 100;
    DWORD dwWzBufferlen = (sizeof(wzBuffer) / sizeof(wzBuffer[0])) * 2;

    WideCharToMultiByte(CP_ACP, 0,wzBuffer, dwWzBufferlen, szBuff, szBufflen, 0, 0);

    printf("%s \r\n", szBuff);
    system("pause");

    return 0;
}

注意要设置Unicode字符集.如果不设置,则TCHAR相当于是char类型.所以转换就是乱码.

原文地址:https://www.cnblogs.com/iBinary/p/10758683.html

时间: 2024-10-09 02:22:39

Windows核心编程第二章,字符串的表示以及宽窄字符的转换的相关文章

Windows核心编程第一章.错误处理

Windows核心编程第一章,错误处理. 一丶错误处理 1.核心编程学习总结 不管是做逆向,开始做开发.在Windows下.你都需要看一下核心编程这本书.这本书确实写得很好.所以自己在学习这本书的同时,也把自己所学的知识进行 总结,以及巩固. 2.常见的Windows函数返回类型总结 数据类型 作用 VOID 如果是Void表示函数不可能失败.极少数windows函数会返回void BOOL 表示这个函数会有失败情况.0失败.否则就是非0.但是一般都会使用TRUE FALSE来判断. HANDL

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

前沿 学习Windows核心编程是步入Windows编程殿堂的必经之路,2018年寒假重温了计算机操作系统知识,前阵子又过学习Windows程序设计方面的基础,正所谓打铁要乘热,所以我又入了Windows核心编程的坑啦,哈哈~ 学习目标 每一章的学习都要明确一个目标,就是你学完这一章之后你能做些什么?好的,我们一步步来学习第一章节错误处理.以下是这一章节的学习目标:1.了解Windows函数的错误机制2.了解GetLastError和SetLastError函数的使用3.了解FormatMess

Windows核心编程之核心总结(第三章 内核对象)(2018.6.2)

学习目标 第三章内核对象的概念较为抽象,理解起来着实不易,我不断上网找资料和看视频,才基本理解了内核对象的概念和特性,其实整本书给我的感觉就是完整代码太少了,没有多少实践的代码对内容的实现,而且书本给的源码例子,有太多我们不知道的知识,并且这些知识对本章主要内容来说是多余的,所以我们理解起来也非常困难.为了更好的学习这章,我补充了一些辅助性内容.这一章的学习目标:1.Windows会话和安全机制2.什么是内核对象?3.使用计数和安全描述符4.内核对象句柄表5.创建内核对象6.关闭内核对象7.跨进

读书笔记----《windows核心编程》第三章 内核对象1(句柄与安全性)

最近一直没有更新博客,因为一直在想一个问题,内核对象这一章内容很多很重要,自己没有掌握好也没有把握写好这一章,最后还是决定能写多少写多少,一面写一面学,后续学到新的再更新吧; <windows核心编程>提了几种内核对象: 访问令牌对象:与windows的安全性有关,目前不是很懂,了解后再写; 事件对象: Event对象,可跨进程同步; 由CreateEvent创建; 文件对象: File对象,比较常见; 由CreateFile创建; 文件映射对象: 通过文件映射可以方便的操作文件(如同文件数据

【转】《windows核心编程》读书笔记

这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入,但应该是合理的.开头几章由于我追求简洁,往往是很多单独的字句,后面的内容更为连贯. 海量细节. 第1章    错误处理 1.         GetLastError返回的是最后的错误码,即更早的错误码可能被覆盖. 2.         GetLastError可能用于描述成功的原因(CreatEvent)

《Windows核心编程》读书笔记 上

[C++]<Windows核心编程>读书笔记 这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入,但应该是合理的.开头几章由于我追求简洁,往往是很多单独的字句,后面的内容更为连贯. 海量细节. 第1章    错误处理 1.         GetLastError返回的是最后的错误码,即更早的错误码可能被覆盖. 2.         GetLas

C++Windows核心编程读书笔记

转自:http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E6%96%87/71405.shtml "C++Windows核心编程读书笔记": 关键词:c++windows 核心 编程 读书笔记 这篇笔记是我在读<windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和windows实际机制可能有出入,但应该是合理的.开头几章由于我追求简洁

python基础学习07(核心编程第二版)部分

# -*- coding: utf-8 -*- # ==================== #File: python #Author: python #Date: 2014 #==================== __author__ = 'Administrator' #file与input output #文件对象 #简单说来,就是写入和读取的方式 #file(),open()2个操作都是一样的,一般推荐open() #语法 # open(name[, mode[, bufferin

windows核心编程 DLL技术 【转】

注:本文章转载于网络,源地址为:http://blog.csdn.net/ithzhang/article/details/7051558 本篇文章将介绍DLL显式链接的过程和模块基地址重定位及模块绑定的技术. 第一种将DLL映射到进程地址空间的方式是直接在源代码中引用DLL中所包含的函数或是变量,DLL在程序运行后由加载程序隐式的载入,此种方式被称为隐式链接. 第二种方式是在程序运行时,通过调用API显式的载入所需要的DLL,并显式的链接所想要链接的符号.换句话说,程序在运行时,其中的一个线程