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. 最被低估的Python绘图库!Matlplotlib 超强实力鉴赏

    最被低估的Python绘图库!Matlplotlib 超强实力鉴赏 Matplotlib Matplotlib 是一个 Python 的 2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量 ...

  2. ORM之Sequelize

    一.环境: Vue.Quasar.Electron.Postgres.Sequelize.sequelize-auto 二.安装 (1)添加Sequelize(全局安装) $npm install - ...

  3. 聊聊时下火热的 AIGC 与 Web3

    近期,AI 已经开始影响到了音乐行业,在 B 站搜索"AI 孙燕姿",从流行歌曲到摇滚,从周杰伦到王力宏,没有 AI 孙燕姿驾驭不了的歌曲. 有用户评论:"我感觉 AI ...

  4. Spark 数据倾斜及其解决方案

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/lqMu6lfk-Ny1ZHYruEeBdA 作者简介:郑志彬,毕业于华南理工大学计算机科学与技术(双 ...

  5. 二、mysql安装(主从)

    上一篇: 一.mysql5.7 rpm 安装(单机)  https://www.cnblogs.com/yclh/p/14951314.html 环境      192.168.0.1 主节点     ...

  6. jmeter+badboy软件安装与使用

    目录: 一.软件下载安装 1.软件下载 2.安装过程 3.安装完成后界面,默认是已经开启录制模式 二.操作使用 1.访问sougou 2.搜索:mrwhite2020 3.选择导出jmx格式 4.使用 ...

  7. python进阶(8)--测试函数与类

    一.测试函数(unittest) 1.源文件方法(name_function.py): def get_formatted_name(first,last): """生成 ...

  8. 小白学标准库之 mux

    本文介绍第三方库 gorilla/mux,相比于 Go 自带的 net/http 它能提供更为强大的路由处理功能. mux 表示 HTTP request multiplexer (HTTP 请求多路 ...

  9. python json.loads()、json.dumps()和json.dump()、json.load()区别

    json.loads().json.dumps()和json.dump().json.load()分别是两组不同用法 带s的用于数据类型的转换,不带s的用于操作文件. json.loads().jso ...

  10. 【LINT】cpplint 分析笔记

    cpplint 分析笔记 · [前提得看下google规范] @2022-1-13 20:44:48 error message formate: [filename] [linenum] [mess ...