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. A/B实验背后的秘密:样本量计算

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 一.前言 背景: AB实验具有一定前瞻性,统计性,科学性的特性.用好了就实现了在大数据时代的充分利用数据分析问题, ...

  2. Flutter如何有效地退出程序

    今天博主来谈一个开发Flutter App的小技巧--怎样有效地退出程序. 这种方法典型的应用场景就是用户许可协议的同意与否.从用户的角度讲,虽然大部分人都会无脑点击"同意",但我 ...

  3. NOKOV度量动作捕捉协助完成无人机室内定位研究

    随着工业发展.技术进步,无人机的使用在各行各业愈发普遍,开始出现无人机飞行送外卖.智能无人机自主巡检等多方面应用.在这一过程中,无人机飞行定位就成为了重中之重. 西北工业大学无人机特种技术国防科技重点 ...

  4. POJ 1456 Supermarket【贪心 + 并查集】

    http://poj.org/problem?id=1456 题意:给你 N 件不同的商品,每件商品最多可以买一次.每件物品对应两个值 pi di pi 表示物品的价值,di 表示可以买的最迟时间(也 ...

  5. [kuangbin]专题九 连通图 题解+总结

    kuangbin专题链接:https://vjudge.net/article/752 kuangbin专题十二 基础DP1 题解+总结:https://www.cnblogs.com/RioTian ...

  6. 2019CCPC-江西省赛(重现赛)队伍题解

    2019CCPC江西省赛(重现赛) 第一次组队(和队内dalao:hzf)参加比赛,这次比赛使用的是我的笔电,但因为我来的比较晚,没有提前磨合:比如我的64键位键盘导致hzf突然上手不习惯. Solv ...

  7. 【每日一题】14.Accumulation Degree(树形DP + 二次扫描)

    补题链接:Here 一个树形水系,有 \(n\) 个结点,根结点称为源点,叶子结点称为汇点,每条边都有水量限制$C(x,y) \((\)x,y$ 为这条边的两个端点),源点单位时间流出的水量称为整个水 ...

  8. SAE 最佳实践范本:助力视野数科进入云原生“快车道”

    阿里云生态金融科技行业标杆 -- ​ 2021 年,云原生的商业价值正在被加速释放. ​ 一个公认的事实是,Serverless 是当下云原生方向内绝对的亮点.可以看作,它的出现,让企业用户真正地免除 ...

  9. <vue 基础知识 6、条件判断标签v-if>

    代码结构 一.     01-v-if用法 1.效果 根据分数的不同展现不同的汉字 2.代码 01-v-if用法.html <!DOCTYPE html> <html lang=&q ...

  10. vue中form组件中上传el-upload(单、多附件上传,以及上传回显以及编辑不出现等问题)

    https://blog.csdn.net/weixin_46565787/article/details/121934075?spm=1001.2101.3001.6650.2&utm_me ...