1. ANSI 和 Unicode

Windows 中涉及字符串的函数有两个版本

1)ANSI版本的函数会把字符串转换为Unicode形式,再从内部调用函数的Unicode版本

2)Unicode版本会在内部调用ANSI版本

:关于CreateFile有两个版本

CreateFileW 接受Unicode字符串

#ifdef UNICODE
#define CreateFile CreateFileW
#else
#define CreateFile CreateFileA
#endif WINBASEAPI
HANDLE
WINAPI
CreateFileW(
_In_ LPCWSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);

CreateFileA 接受ANSI字符串:内部转为Unicode,CreateFileA->CreateFileW

WINBASEAPI
HANDLE
WINAPI
CreateFileA(
_In_ LPCSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);

2. 安全字符串函数

​ 若目标字符串缓冲区不够大,无法容纳新生成的字符串,就会导致内存中的数据被破坏(memory corruption)



strcpy 和 wcscpy函数(以及其他大多数字符串处理函数)

问题:没有收到指定了的缓冲区最大长度的参数,即函数不知道自己会出错,所以也不报错

1)新的安全字符串函数

​ ①包含StrSafe.h时,string.h也会被包括进来。C运行库中现有的字符串处理函数已被标记为废弃不用,如果使用这些函数,编译时就会发出警告

​ ②现有的每一个函数都有一个对应的新版本函数,末尾加上_s后缀(s代表source)

​ ③将一个可写的缓冲区作为参数传递时,必须提供大小,可以对缓冲区使用_countof宏,很容易计算

#define _tcscat         wcscat
#define _tcscat_s wcscat_s
#define _tcscpy wcscpy
#define _tcscpy_s wcscpy_s

④所有安全函数的首要任务:验证传入的参数值(如以下源码的几个判断)

// wcscpy_s函数的源码:
INT CDECL wcscpy_s(wchar_t* wcDest, size_t numElement, const wchar_t *wcSrc)//characters
{
size_t size = 0; if (!wcDest || !numElement)
return EINVAL; wcDest[0] = 0; if (!wcSrc)
{
return EINVAL;
} size = strlenW(wcSrc) + 1;//所以传入的字符数不用包括'\0',此处已经+1 if (size > numElement)
{
return ERANGE;
} memcpy(wcDest, wcSrc, size * sizeof(WCHAR));//bytes return 0;
}

2)处理字符串时获得更多控制:格式化函数

_stprintf(BufferData, _T("我们都是中国人 %d %f"), v1,v2);
_stscanf(BufferData, _T("%s %d %f"),v5,&v3,&v4);

3)Windows字符串函数

​ 字符串比较函数CompareString(Ex)和CompareStringOrdinal

​ CompareString(Ex)函数:需要以符合用户语言习惯的方式向用户显示的字符串

int CompareString (
LCID locale,
DWORD dwCmdFlags,
PCTSTR pString1,
int cch1,
PCTSTR pString2,
int cch2);

​ CompareStringOrdinal函数:比较内部所用的字符串(如路径名、注册表项/值1、XML元素属性等)

int CompareStringOrdinal (
PCWSTR pstring1,
int cchCount1,
PCWSTR pString2,
int cchCount2 ,
BOOL bIgnoreCase) ;

3. 支持单双字的方式(自己宏定义)

1)string类的单双字

​ wstring为支持双字的string类

​ string为支持单字的string类

#ifdef _UNICODE
#define _tstring wstring
#else
#define _tstring string
#endif

2)输入输出的单双字

​ wcout和wcin支持双字

​ cout和cin支持单字

#ifdef _UNICODE
#define _tcout wcout
#define _cin wcin
#else
#define _tcout cout
#define _cin cin
#endif

4. 1-3 部分完整的测试代码

#include <windows.h>
#include <iostream>
#include <tchar.h>
#include <atlstr.h> using namespace std; void Sub_1();
void Sub_2();
void Sub_3();
void Sub_4();
void Sub_5(); void _tmain()
{
_tsetlocale(LC_ALL, _T("Chinese-simplified")); //Sub_1();
//Sub_2();
//Sub_3();
//Sub_4(); //Sub_5();
} //字符串拷贝
void Sub_1()
{
TCHAR v1[20] = _T("HelloWorld"); //20 20 20 40
TCHAR v2[20] = {0}; //NumberOfCharacters
_tcscpy_s(v2, 11, v1); //第一参数为目标地址,第二参数为字符的数量,第三参数为源地址
_tprintf(_T("%s\r\n"), v2); //Count Bytes Of Source to Destination //memcpy(v2, v1, sizeof(TCHAR)*(10+1)); //第一参数为目标地址,第二参数为源地址,第三参数为字节大小
//_tprintf(_T("%s\r\n"), v2); }
//sizeof和countf的区别
void Sub_2()
{
int v1[5] = { 1,2,3,4,5 };
TCHAR v2[] = _T("HellWorld");
//#define _countof(array) (sizeof(array)/sizeof(array[0])) sizeof(v1)/sizeof(v1[0])
_tprintf(_T("%d %d\r\n"), sizeof(v1), _countof(v1)); //结果为20,5
_tprintf(_T("%d %d\r\n"), sizeof(v2), _countof(v2)); //结果为20,10 } //宏定义一个支持单双字的_tcout
void Sub_3()
{
#ifdef _UNICODE
#define _tstring wstring
#define _tcout wcout
#define _cin wcin
#else
#define _tstring string
#define _tcout cout
#define _cin cin
#endif
//_tstring v1 = _T("HelloString");
//_tcout << v1.c_str() << endl;
//_tprintf(_T("%s\r\n"), v1.c_str()); CString v2 = _T("HelloCString"); //Cstring支持单双字
_tcout << v2.GetString() << endl;
} //字符串拷贝函数_tcscpy和memcpy的区别,memcpy可以重叠拷贝(自身前面的数据向后拷贝)
void Sub_4()
{
//重叠拷贝 TCHAR v1[20] = _T("HelloWorld"); //_tcscpy_s(&v1[0], 10, &v1[1]);
_tcscpy_s(&v1[1], 10, &v1[0]); //程序崩溃
_tcscpy(&v1[1], &v1[0]); memcpy(&v1[1], &v1[0],(_tcslen(v1)+1)*sizeof(TCHAR));
_tprintf(_T("%s\r\n"), v1);
}
//_stprintf和_stscanf 字符串格式化
void Sub_5()
{
int v1 = 345;
float v2 = 4.14;
TCHAR BufferData[0x1000] = { 0 };
_stprintf(BufferData, _T("我们都是中国人 %d %f"), v1,v2);
_tprintf(_T("_tprintf:%s\r\n"), BufferData); //输出打印 int v3 = 0;
float v4 = 0;
TCHAR v5[0x1000];
_stscanf(BufferData, _T("%s %d %f"),v5,&v3,&v4);
_tprintf(_T("%d\r\n"),v3);
_tprintf(_T("%f\r\n"), v4);
_tprintf(_T("%s\r\n"), v5); }

5. 字符编码

5.1 UTF-16编码

​ 每个Unicode字符都使用UTF-16编码,UTF的全称是Unicode Transformation Format(Unicode转换格式)。UTF-16将每个字符编码为2个字节(或者说16位)。

1)16位不足以表示某些语言的所有字符,此时,UTF-16支持使用代理(surrogate),后者是用32位(4个字节)来表示一个字符的一种方式。

2)只有少数应用程序需要表示这些语言中的字符,所以UTF-16在节省空间和简化编码这两个目标之间,提供了一个很好的折衷。

3)注意:

.NET Framework始终使用UTF-16来编码所有字符和字符串,如果需要在本机代码(native code)和托管代码(managed code)之间传递字符或字符串,使用UTF-16能改进性能和减少内存消耗。

5.2 UTF-8编码

​ UTF-8将一些字符编码为1个字节,一些字符编码为2个字节,一些字符编码为3个字节,一些字符编码为4个字节。

​ 值在Ox0080以下的字符压缩为1个字节,这对美国使用的字符非常适合。0x0080和 0x07FF之间的字符转换为2个字节,这对欧洲和中东地区的语言非常适用。0x0800 以上的字符都转换为3个字节,适合东亚地区的语言。最后,代理对(surrogate pair)被写为4个字节。UTF-8在对值为0x0800及以上的大量字符进行编码的时候,不如UTF-16高效。

5.3 UTF-32编码

​ UTF-32将每个字符都编码为4个字节。

适用场景

如果打算遍历字符(任何语言中使用的字符),但又不想处理字节数不定的字符,这种编码方式非常有用。

​ 例如,采用UTF-32编码方式,不需要关心代理(surrogate)的问题,每个字符都是4个字符。

​ 从内存使用这个角度来看,UTF-32并不是一种高效的编码格式。这种编码格式一般在应用程序内部使用。

5.4 Unicode 字符集和字母表

6位代码 字符 16位代码 字母/书写符号
0000-007F ASCII 0300-036F 常见的变音符号
0080-00FF 西欧语系字母 0400-04FF 西里尔字母
0100-017F 欧洲拉丁字母 0530-058F 亚美尼亚文
0180-01FF 扩充拉丁字母 0590-05FF 希伯来文
0250-02AF 标准音标 0600-06FF 阿拉伯文
02B0-02FF 进格修饰字母 0900-097F 梵文字母

6. Unicode 与 ANSI 字符串转换

6.1 MultiByteToWideChar 函数 ANSI->UNICODE

​ 将多字节字符串转换为宽字符字符串

int MultiByteToWidechar(
UINT uCodePage,
DWORD dwFlags,
PCSTR pMultiByteStr,
int cbMultiByte,
PWSTR pWideCharStr,
int cchWideChar) ;

6.2 WideCharToMultiByte 函数 UNICODE->AMSI

​ 将宽字符字符串转换为多字节字符串

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

6.3 字符串转换函数(自己定义的)

6.3.1 返回值为 BOOL 参数为 char* 和 WCHAR*

BOOL SeAnsiToUnicode(char* MultiByteString,WCHAR** WideString)
{ DWORD WideStringLength;
if (!MultiByteString)
{
return FALSE;
} WideStringLength = MultiByteToWideChar(CP_ACP, 0, MultiByteString, -1, NULL, 0);
*WideString = (WCHAR*)malloc(WideStringLength * sizeof(WCHAR));
if (*WideString == NULL)
{
return FALSE;
}
MultiByteToWideChar(CP_ACP, 0, MultiByteString, -1, *WideString, WideStringLength);
return TRUE; }
BOOL SeUnicodeToAnsi(WCHAR* WideString, char** MultiByteString)
{ DWORD MultiByteStringLength; if (!WideString)
{
return FALSE;
} MultiByteStringLength = WideCharToMultiByte(CP_ACP,
WC_COMPOSITECHECK ,
WideString, -1, NULL, 0, NULL, NULL); *MultiByteString = (char*)malloc(MultiByteStringLength * sizeof(CHAR));
if (*MultiByteString == NULL)
{
return FALSE;
}
WideCharToMultiByte(CP_ACP,
WC_COMPOSITECHECK,
WideString, -1, *MultiByteString, MultiByteStringLength, NULL, NULL); return TRUE; }

6.3.2 返回值为 DWORD 参数为 LPCSTR 和 LPWSTR

DWORD
SeAnsiToUnicode(LPCSTR AnsiString,
LPWSTR *UnicodeString)
{
INT v7;
INT IsOk = 0; v7 = strlen(AnsiString) + 1;
*UnicodeString = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, v7 * sizeof(WCHAR));
if (*UnicodeString)
{
IsOk = MultiByteToWideChar(CP_ACP,
0,
AnsiString,
-1,
*UnicodeString,
v7);
} return IsOk; } DWORD
SeUnicodeToAnsi(LPCWSTR UnicodeString,
LPSTR *AnsiString)
{
INT v7;
INT IsOk = 0; v7 = wcslen(UnicodeString) + 1; *AnsiString = (LPSTR)HeapAlloc(GetProcessHeap(), 0, v7);
if (*AnsiString)
{
IsOk = WideCharToMultiByte(CP_ACP,
0,
UnicodeString,
-1,
*AnsiString,
v7,
NULL,
NULL);
} return IsOk; }

6.3.3 返回值为 String 参数为 LPCSTR 和 LPWSTR

wstring __SeAnsiToUnicode(const char *MultiByteString)
{
size_t v7 = strlen(MultiByteString);
wchar_t* v5 = new wchar_t[v7+1];
mbstowcs(v5, MultiByteString, v7);
v5[v7] = 0x00;
wstring Object(v5);
delete[] v5;
return Object;
}
string __SeUnicodeToAnsi(const wchar_t *WideString) {
size_t v7 = wcslen(WideString);
char* v5 = new char[v7+1];
wcstombs(v5, WideString, v7);
v5[v7] = 0;
string Object(v5);
delete[] v5;
return Object;
}

6.4 ANSI与Unicode字符串的初始化

VOID SeRtlInitAnsiString(IN OUT PANSI_STRING AnsiString,
IN char* SourceString)
{
#define MAX_USHORT 0xffff
SIZE_T v7; if (SourceString)
{
v7 = strlen(SourceString);
if (v7 > (MAX_USHORT - sizeof(CHAR)))
{
v7 = MAX_USHORT - sizeof(CHAR);
}
AnsiString->Length = (USHORT)v7;
AnsiString->MaximumLength = (USHORT)v7 + sizeof(CHAR);
}
else
{
AnsiString->Length = 0;
AnsiString->MaximumLength = 0;
} AnsiString->Buffer = (PCHAR)SourceString;
}
VOID SeRtlInitUnicodeString(
IN OUT PUNICODE_STRING UnicodeString,
IN PCWSTR SourceString)
{
#define MAX_USHORT 0xffff
SIZE_T v7;
CONST SIZE_T MaxSize = (MAX_USHORT & ~1) - sizeof(UNICODE_NULL); // an even number if (SourceString)
{
v7 = wcslen(SourceString) * sizeof(WCHAR); if (v7 > MaxSize)
v7 = MaxSize;
UnicodeString->Length = (USHORT)v7;
UnicodeString->MaximumLength = (USHORT)v7 + sizeof(UNICODE_NULL);
}
else
{
UnicodeString->Length = 0;
UnicodeString->MaximumLength = 0;
} UnicodeString->Buffer = (PWCHAR)SourceString;
} typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING; typedef struct _ANSI_STRING {
USHORT Length;
USHORT MaximumLength;
PSTR Buffer;
} ANSI_STRING;
typedef ANSI_STRING *PANSI_STRING;

Windows 核心编程笔记 [2] 字符串的更多相关文章

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

    目录 Windows核心编程,字符串的表示以及宽窄字符的转换 1.字符集 1.1.双字节字符集DBCS 1.2 Unicode字符集 1.3 UTF-8编码 1.4 UTF - 32编码. 1.5 U ...

  2. Windows核心编程笔记之处理字符串

    0x01 ANSI 和宽字符定义 // ANSI 字符定义 CHAR varChar_1 = 'a'; // #typedef char CHAR CHAR varChar_2[] = "A ...

  3. Windows核心编程笔记之进程

    改变进程基址,获取进程基址 #include <Windows.h> #include <iostream> #include <strsafe.h> #inclu ...

  4. Windows核心编程笔记之内核对象

    0x01 子进程继承父进程内核对象句柄 父进程 #include <Windows.h> #include <iostream> #include <strsafe.h& ...

  5. Windows核心编程笔记之错误处理

    0x01 GetLastError() 函数用于获取上一个操作的错误代码 #include <Windows.h> #include <iostream> using name ...

  6. Windows核心编程笔记之作业

    创建作业,并加以限制 HANDLE WINAPI CreateJob() { BOOL IsInJob = FALSE; DWORD ErrorCode = NULL; // 不能将已经在作业中的进程 ...

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

    [C++]<Windows核心编程>读书笔记 这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对 ...

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

    转自:http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E6%96%87/71405.shtml "C++Windows核心编程读书笔 ...

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

    这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入 ...

  10. windows核心编程---第二章 字符和字符串处理

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

随机推荐

  1. Java SpringBoot Bean InitializingBean 项目初始化

    Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean.工厂Bean跟普通Bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂Bean的ge ...

  2. PPT 工作需求:如何利用PPT做活动海报&H5?

    PPT 工作需求:如何利用PPT做活动海报? 图片素材 + 字体 + 封面排版 PPT 工作需求:如何利用PPT制作H5? https://www.maka.im/muban http://www.p ...

  3. OpenFeign FormData

    服务端接口代码如下: /** * 上传数据+实体信息 */ @RequestMapping("/upload") public String doctorAnalysis(Http ...

  4. pytest+request+allure生成测试报告

    基本流程 模拟数据 url,paras,method,except http://www.baidu.com, {k=12}, get, 200 请求url (接口文档) 参数 请求方法 预期返回响应 ...

  5. hyper-v虚拟机中ubuntu连不上网络的解决办法

    首先重启下hyper-v的服务,看下情况: 1.检查hyper-v相关的服务有没有开启 2.如果开启了服务,unbuntu仍然不能连网,则在ubtuntu中进行接下来的步骤: 2.1 设置网络连接为N ...

  6. 一个NASA、Google都在用的开源CMS:wagtail

    说起开源CMS,你会想到哪些呢?WordPress?DoraCMS?joomla? 今天再给大家推荐一个非常好用的开源CMS:Wagtail 如果您正在选型的话,可以了解一下Wagtail的特点: 基 ...

  7. Windows | 安装 Docker 遇到的 WSL 2 installation is incomplete 报错的解决方案

    控制面板中打开 Windows功能,在其中勾选 适用于 Linux 的 Windows 子系统 下载 WSL 更新包(非最新版本的也会报错) 更新包下载链接:https://wslstorestora ...

  8. IDEA | 使用Maven创建Web项目并配置Tomcat

    学习这种方式的原因是以后Tomcat中运行的绝大多数都是Web项目,而使用Maven工具能更加简单快捷的把Web项目给创建出来,所以Maven的Web项目具体如何来构建呢? 在真正创建Maven We ...

  9. springboot启动类源码探索一波

    举个例子:  这是一个原始的Spring IOC容器启动方法,我们需要AnnotationConfigApplicationContext这个类有如下几个步骤 1. 创建构造方法,根据我们所传入的Ap ...

  10. 绿色数字园区运维:一屏群集 3D 可视化智慧楼宇

    前言 在"新基建"驱动的数字经济热潮下,智慧园区建设发展成为实现园区管理绿色化.现代化.智慧化的核心抓手.通过利用云计算.物联网.大数据等新一代技术手段,充分聚合园区内各类资源,全 ...