zoukankan      html  css  js  c++  java
  • 《Windows via C/C++》学习笔记(二):Working with Characters and String

    1. 字符编码与数据类型

    编码

    字节数

    类型

    字符(串)常量

    WinNT.h中的定义

    ANSI

    8bit

    char

    'A'

    "A string"

    typedef char CHAR;

    typedef CHAR *PCHAR;

    typedef CHAR *PSTR;

    typedef CONST CHAR *PCSTR;

    Unicode (UTF-16)

    16bit

    wchar_t

    通过编译器设置/Zc:wchar_t支持

    L'A'

    L"A string"

    typedef char CHAR;

    typedef CHAR *PWCHAR;

    typedef CHAR *PWSTR;

    typedef CONST CHAR *PCWSTR;

    WinNT.h头文件中定义字符类型宏,TCHAR、PTCHAR、PTSTR、PCTSTR、TEXT(),可在ANSI和Unicode编码间通用。

    2. Windows函数中关于字符编码的信息

    • 使用UNICODE宏区分程序的编码是使用ANSI或Unicode;
    • 在Vista中ANSI编码的函数在内部首先将ANSI转化为Unicode再调用Unicode版本去做;
    • 为兼容16位的旧Windows函数只有ANSI编码版本,应被替换掉,如OpenFile -> CreateFile, WinExec -> CreateProcess等;
    • 新的Windows函数只有Unicode版本,如ReadDirectoryChangesW和CreateProcessWithLogonW;
    • COM接口要求使用Unicode;
    • 资源文件通常为Unicode编码,Vista下系统可自动转换编码。

    3. C运行时库中关于字符编码的信息

    • C运行时库提供Unicode、ANSI两个版本,但两者不会相互调用;
    • 使用_UNICODE宏区分Unicode和ANSI编码。

    4. C运行时库的安全字符函数

    潜在隐患:目标字符缓冲区未能承载结果字符,导致内存崩溃。

    解决方法:包含StrSafe.h头文件

    a. 使用_tcscpy_s、_tcscat_s代替_tcscpy、_tcscat,其函数原型为

    errno_t _tcscpy_s(PTSTR strDestination, size_t numberofCharacters, PCSTR strSource);

    errno_t _tcscat_s(PTSTR strDestination, size_t numberofCharacters, PCSTR strSource);

    • 其中参数numberofCharacters使用_countof宏(在stdlib.h中定义)计算字符的数目去确定缓存大小;
    • 安全函数返回errno_t,但并非真正返回。在Debug模式下编译的程序会弹出一断言对话框,Release模式下编译的程序会直接退出。

    错误处理方法:

    ① 定义一错误处理函数,函数原型如下:

    void InvalidParameterHandler(PCSTR expression, PCSTR function, PCSTR file, unsigned in line, uintptr_t /*pReserved*/);

    ② 使用_set_invalid_parameter_handler注册处理函数;

    ③ 程序开始时,调用_CrtSetReportMode(_CRT_ASSERT, 0)关闭断言对话框。

    • 当字符串经过安全函数处理时出现错误后,字符串首字符变为'\0',缓冲区的其余部分由0xfd填充。

    b. 更多对字符处理的控制

    包括以字符数目计算缓冲区大小的函数,函数名中带有Cch(count of characters)

    StringCchCat(Ex),StringCchCopy(Ex),StringCchPrintf(Ex)

    和以字节数计算缓冲区大小的函数,函数名中带有Cb(count of bytes)

    StringCbCat(Ex),StringCbCopy(Ex),StringCbPrintf(Ex)

    • 函数返回值类型为HRESULT

    S_OK:表示字符处理成功;

    STRSAFE_E_INVALID_PARAMETER:非法参数,如pszDest为NULL、错误的dwFlags等;

    STRSAFE_E_INSUFFICIENT_BUFFER

    :目标字符串缓冲区空间不足,但目标字符串会尽可能放入字符并以'\0'结束字符串。

    • 以Ex结尾的字符串处理函数提供了更多的控制,增加了三个参数:

    LPTSTR* ppszDestEnd:返回目的字符串缓冲区结束字符('\0')的字符指针;

    size_t* pcchRemaining:返回目的字符串缓冲区未使用字符数;

    DWORD dwFlags:关于字符处理的标记。

    数值

    描述

    STRSAFE_FILL_BEHIND_NULL

    当函数运行成功时,将dwFlags低字节填充到目的字符串未初始化的字符上。

    STRSAFE_IGNORE_NULLS

    将NULL字符串指针视为(TEXT(""))。

    STRSAFE_FILL_ON_FAILURE

    当函数运行失败时,将dwFlags低字节填充到整个目的字符串上,除了第一个字符设置为'\0'。

    STRSAFE_NULL_ON_FAILURE

    当函数运行失败时,将目的字符串的第一个字符设置为'\0',并将字符串视为(TEXT(""))。

    STRSAFE_NO_TRUNCATION

    目的字符串被设置为(TEXT(""))。

    c. Windows字符函数关于字符串比较的函数

    可使用CompareString(Ex)和CompareStringOrdinal比较字符串:

    • CompareString(Ex)是根据系统的区域设置,从语言学角度去比较两个字符串。函数需要获得一32位的区域设置编号(LCID),可通过GetThreadLocale函数获得。
    • CompareStringOrdinal则是针对程序代码中的字符串比较,只做码点比较(code-point comparison)。该函数只有Unicode编码的版本。
    • 返回值为int。可将返回值减2,即可得到与C运行时库中字符串比较函数相同的返回值。

    数值

    描述

     

    0

    字符串比较操作失败。

    CSTR_LESS_THAN

    1

    pString1小于pString2。

    CSTR_EQUAL

    2

    字符串相等。

    CSTR_GREATER_THAN

    3

    pString1大于pString2。

    5. 使用Unicode的理由

    • 便于程序的全球化和本地化;
    • 可发布独立的支持所有语言的二进制文件;
    • 提高程序运行的效率和减少内存占用(部分Windows函数会在内部将ANSI字符串转为Unicode再继续运行);
    • 某些Windows函数只提供Unicode编码版本;
    • 可确保程序能够与COM整合;
    • 可确保程序能够与.Net Framework整合;
    • 便于程序资源操作(资源中的字符串通常为Unicode编码)。

    6. 使用字符及字符串的推荐方法

    • 以字母数组的角度,而非char数组和字节数组的角度去看待字符串;
    • 对于文本字符和字符串,使用通用的数据类型,如TCHAR/PTSTR;
    • 对于字节,字节指针和数据缓冲区,使用明确的数据类型,如BYTE/PBYTE;
    • 对于原义字符及字符串使用TEXT()或_T()宏,但不要混合使用;
    • 全球化,如使用PTSTR代替PSTR;
    • 使用_countof()宏计算字符个数,多用于确定缓冲区大小。使用_sizeof()计算字节数,多用于分配内存空间;
    • 避免printf系列函数,特别是使用%s和%S去实现ANSI和Unicode相互转换,应使用MultiByteToWideChar和WideCharToMultiByte;
    • 总是同时使用或不使用UNICODE和_UNICODE宏。

    7. 操作字符串的推荐方法

    • 总是使用安全函数(以_s为后缀或以StringCch为前缀的函数);
    • 不要使用不安全的C运行时库函数,即没有以目标字符串大小为函数参数的函数。

    对于缓冲区的操作,C运行时库提供了相应的更新版本,例如memcpy_s,memmove_s,wmemcpy_s,wmemmove_s。使用这些函数,需要声明__STDC_WANT_SECURE_LIB__宏和包含CrtDefs.h;

    • 使用编译器设置/GS和/RTCs;
    • 不使用Kernel32中的方法,例如lstrcat和lstrcpy;
    • 程序代码中使用的字符串的比较使用CompareStringOrdinal,用户界面上的字符串比较使用CompareString(Ex)。

    8. Unicode与ANSI的相互转化

    • 使用MultiByteToWideChar,将多字节字符串转化为宽字符串,函数原型为:

    int MultiByteToWideChar(UINT uCodePage, DWORD dwFlag, PCSTR pMultiByteStr, int cbMultiByte, PWSTR pWideCharStr, int cchWideChar)

    转换步骤:

    ① 以NULL为pWideCharStr的值、0为cchWideChar的值、-1为cbMultiByte的值调用MultiByteToWideChar,由返回值得到源字符串的字符个数;

    ② 分配内存,大小为源字符串的字符个数×sizeof(wchar_t);

    ③ 再次调用MultiByteToWideChar,将目的字符串缓冲区地址、字符个数等参数传入;

    ④ 使用字符;

    ⑤ 释放字符。

    • 使用WideCharToMultiByte,将宽字符串转化为多字节字符串,函数原型为:

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

    转换步骤:

    与MultiByteToWideChar类似。参数pDef

    aultChar作为默认字符,用于替代未能在多字节字符中找到对应字符的宽字符,pfUsedDefaultChar用于返回是否有宽字符未能找到对应的多字节字符。

    • 通过MultiByteToWideChar和WideCharToMultiByte函数,在实现时只实现Unicode编码版本的函数。通过内部调用MultiByteToWideChar转换再调用Unicode编码版本的方式,实现ANSI编码版本的函数。最后使用宏的方式统一函数命名。

    在CPP文件中:

    void FuncW(PWSTR pWideCharStr, DWORD cchLength)
    
    {
    
    ......
    
    }
    
    void FuncA(PSTR pMultiByteStr, DWORD cchLength)
    
    {
    
    PWSTR pWideCharStr;
    
    int nLenOfWideCharStr;
    
    nLenOfWideCharStr = MultiByteToWideChar(CP_ACP, 0, pMultiByteStr, cchLength, NULL, 0);
    
    pWideCharStr = (PWSTR) HeapAlloc(GetProcessHeap(), 0, nLenOfWideCharStr * sizeof(wchar_t));
    
    If (pWideCharStr == NULL)
    
    return;
    
    MultiByteToWideChar(CP_ACP, 0, pMultiByteStr, cchLength, pWideCharStr, nLenOfWideCharStr);
    
    FuncW(pWideCharStr, cchLength);
    
    WideCharToMultiByte(CP_ACP, 0, pWideCharStr, cchLength, pMultiByteStr, (int)strlen(pMultiByteStr), NULL, NULL);
    
    HeapFree(GetProcessHeap(), 0, pWideCharStr);
    
    return;
    
    }

    在头文件中:

    void FuncW(PWSTR pWideCharStr, DWORD cchLength);
    
    void FuncA(PSTR pMultiByteStr, DWORD cchLength);
    
    #ifdef UNICODE
    
    #define Func FuncW
    
    #else
    
    #define Func FuncA
    
    #endif
    • 区分Unicode与ANSI编码

    使用AdvApi32.dll中,在WinBase.h中定义的IsTextUnicode函数去区分文件使用的是ANSI还是Unicode编码。该函数并未能精确得出文件的编码。其函数原型为:

    BOOL IsTextUnicode(CONST PVOID pvBuffer, int cb, PINT pResult);

  • 相关阅读:
    7
    go http请求库HttpRequest
    Golang设计模式
    深挖 go 之 for-range 排坑指南
    go在并发情况下使用map
    Redis知识点总结
    go 条件与循环结构
    数据分析的数据源
    go 生产者消费者模型与发布订阅模型
    go 文件与目录操作
  • 原文地址:https://www.cnblogs.com/alonecat06/p/2710658.html
Copyright © 2011-2022 走看看