读Windows核心编程2字符和字符串

(2)字符和字符串处理

字符编码

ANSI为单字节编码,所以最多表示256个符号,这远远不够,因此双字节字符集(double-byte character set, DBCS)应运而生,其原理是当第一个字节在某一范围内时,需要继续检测第二个字节才能确定这两个字节代表什么符号,以日本字为例,首字节在0x81~0x9F或0xE0~0xFC,那么就需要检测下一个字节。这样的话,有的符号一个字节表示,有的是两个字节表示。单单使用strlen也无法确定字符串到底有多少字符,它只告诉你到结尾的0之前有多少个字节。

Unicode标准包含以下几种编码转换格式(Unicode Transformation Format):

UTF-8:

值在0x0080以下的,压缩为1个字节,适合于美国使用的字符;

0x0080~0x07FF转换为2个字节,适合于欧洲和中东地区的语言;

0x0800以上转换为3个字节,适合于亚洲语言;

代理对(surrogate pair)被写为4个字节。

由于UTF-8对ANSI字符的兼容性,并且在ANSI字符较多的情况下更节省空间,因此比较流行,然后在字符串中存在大量0x0800以上的字符时,UTF-16比较高效;

UTF-16:

    所有字符以2个字节表示,对于2字节还不够的语言使用代理(surrogate)来表示,后者使用4个字节来表达一个字符,但由于只有少数语言才会用到,因而2字节编码方式多数情况下比较高效。Windows内核/COM/.NET都是采用UTF-16编码。

UTF-32:

    每个字符都采用4字节表示,这种方式比较简单,如果只在内存中使用的话,UTF-32是个高效的方式,然后如果保存文件或者网络传输的话,就比较浪费空间了。

ANSI字符和Unicode字符

ANSI字符和字符串:

Char c='A';

Char sz[100]="A String";

Unicode字符和字符串:

Wchar_t c=L'A';

Wcahr_t sz[100]=L"A String"

ANSI和Unicode都能编译:

    #ifdef UNICODE

        #define __TEXT(quote) L##quote

        Typedef WCHAR TCHAR;

        Typedef const WCHAR *PCTSTR;

    #else

        #define __TEXT(quote) quote

        Typedef CHAR TCHAR;

        Typedef const CHAR *PCTSTR;

    #endif

 

    #define TEXT(quote) __TEXT(quote)

    #define _T(quote) __TEXT(quote)

    TCHAR c=_T('A');

    TCHAR sz[100]=_T("A String");

Windows的ANSI和Unicode函数

    Windows API使用UNICODE宏来区分ANSI和Unicode的使用。

自Windows NT起,Windows所用版本都用Unicode来构建,所以所有的核心函数都需要Unicode字符串,然而为了兼容性,SDK为大多数API提供ANSI版本,但ANSI版本只是个转换壳,就是说ANSI版本的API在内部分配缓冲区,并将ANSI字符串转成Unicode,然后调用相应的Unicode版本,并将结果Unicode转回ANSI然后释放缓冲区并返回。

    另外,COM接口只接受Unicode字符串。

    资源(字符串表,对话框模板,菜单等)编译完后都是以Unicode字符串保存,如果程序没有定义UNICODE宏,操作系统将执行内部转换。例如,在实际编译模块的时候,LoadString会变成调用LoadStringA,LoadStringA读取资源中的Unicode字符串,并把它转换成ANSI返回给应用程序。

C运行时的ANSI和Unicode函数

    C运行时使用_UNICODE来区分ANSI和Unicode,比Windows API使用的宏多个下划线。

    C运行时的ANSI和Unicode函数都是"自力更生",不象Windows API那样,C运行时的ANSI和Unicode版本不会互相调用。

    Strlen()    // ANSI,string.h

    Wcslen()    // Unicode, string.h

 

#ifdef _UNICODE

#define _tcslen    wcslen    // tchar.h

#else

#define _tcslen    strlen    // tchar.h

#endif

    一些修改字符串的C运行时函数存在缓冲溢出的隐患,因此,Microsoft引入了相应的安全字符串函数,例如,应使用_tcscpy_s来替代_tcscpy。更多字符串安全函数见strsafe.h。

    利用_countof宏(定义与stdlib.h)可以计算字符串长度,使用_set_invalid_parameter_handler来注册自定义的失败处理函数,使用_CrtSetReportMode(_CRT_ASSERT, 0)可以禁用Debug Assertion Failed对话框。

    使用*_s系列字符串安全函数在缓冲区不足时,它们只简单的将目标缓冲区置为'\0',而使用StringCch*和StringCb*系列函数可以在缓冲区截断获得更多的控制。

    CompareString按照字符在LCID所标识的语言中的含义来比较字符串,因此速度慢,适用于需要展示给用户的字符串比较;CompareStringOrdinal按照字符码位比较,速度快,但不考虑区域设置,适用于程序内部的字符串比较。此外这两个函数返回值与C运行时库函数的结果值(-1,0,1)不同:

    0:表示失败;

    CSTR_LESS_THAN = 1;

    CSTR_EQUAL = 2;

    CSTR_GREATER_THAN = 3;

可以将返回值减2来保持一致。

使用Unicode的好处

  • Unicode有利于程序本地化;
  • 只发布一个二进制文件即可支持所有语言;
  • 由于Windows核心API都使用Unicode,因此应用程序使用Unicode可减少转换,提升效率;
  • 避免了误用已经弃用的(deprecated)函数,很多已弃用的函数没有提供Unicode版本;
  • 轻松与COM, .NET集成;
  • 操纵程序自身的资源的效率也得到提升(资源始终是以Unicode保存)。

推荐的字符和字符串处理方式

  • 使用使用TCHAR/PTSTR来表示字符和字符串;
  • 明确区分字符串(char/wchar_t/TCHAR)和字节串(BYTE);
  • 使用TEXT/_T宏来表示字面量字符和字符串;
  • 使用_countof来计算字符串长度,而不是sizeof;
  • 分配缓冲区时始终要乘以sizeof(TCHAR);
  • UNICODE和_UNICODE要么同时定义,要么都不定义;
  • 不使用不安全的字符串函数;
  • 不使用Kernel32的字符串方法,如lstrcat和lstrcpy;

ANSI和Unicode字符串转换

ANSI转换到Unicode

    int WINAPI MultiByteToWideChar(

_In_ UINT CodePage,

_In_ DWORD dwFlags,

_In_NLS_string_(cbMultiByte) LPCCH lpMultiByteStr,

_In_ int cbMultiByte,

_Out_writes_to_opt_(cchWideChar, return) LPWSTR lpWideCharStr,

_In_ int cchWideChar

);

 

Unicode转换到ANSI

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

);

 

判断文本是否为Unicode

BOOL WINAPI IsTextUnicode(

_In_reads_bytes_(iSize) CONST VOID* lpv,

_In_ int iSize,

_Inout_opt_ LPINT lpiResult

);

 

代码示例:

BOOL StringReverseW(PWSTR pW, int cch){

    PWSTR pEnd=pW+wcsnlen_s(pW, cch)-1;

    wchar_t c;

    while(pW<pEnd){

        c=*pW;

        *pW=*pEnd;

        *pEnd=c;

        ++pW;

        --pEnd;

    }

    return TRUE;

}

 

BOOL StringReverseA(PSTR p, int cch){

    PWSTR pW;

    int nLenOfWide;

    BOOL fOK=FALSE;

 

    nLenOfWide=MultiByteToWideChar(CP_ACP, 0, p, -1, NULL, 0);

    pW=(PWSTR)HeapAlloc(GetProcessHeap(), 0, nLenOfWide*sizeof(wchar_t));

    if(NULL==pW)

        return fOK;

    MultiByteToWideChar(CP_ACP, 0, p, cch, pW, nLenOfWide);

    fOK = StringReverseW(pW, nLenOfWide);

    if(fOK){

        WideCharToMultiByte(CP_ACP, 0, pW, nLenOfWide, p, cch, NULL, NULL);

    }

    HeapFree(GetProcessHeap(), 0, pW);

 

    return fOK;

}

 

void test_unicode(){

    std::wcout.imbue(std::locale("chs"));

    wchar_t ws[]=L"Iz";

    int len=_countof(ws)-1;

    int size=sizeof(ws);

    wcout<<(PCTSTR)ws<<endl;

 

    char cs[]="iZ";

    int clen=_countof(cs)-1;

    int csize=sizeof(cs);

    cout<<cs<<endl;

 

    BOOL bIsUnicode=IsTextUnicode(ws, size, NULL);

    BOOL bCIsUnicode=IsTextUnicode(cs, csize, NULL);

 

    if(StringReverseW(ws,size)){

        wcout<<(PCTSTR)ws<<endl;

    }

 

    if(StringReverseA(cs, csize)){

        cout<<cs<<endl;

    }

}

 

参考:

http://blog.csdn.net/ax614/article/details/6694625

http://www.cnblogs.com/fangyukuan/archive/2010/08/19/1804055.html

 

原文地址:https://www.cnblogs.com/dlbrant/p/3100489.html