zoukankan      html  css  js  c++  java
  • 读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

     

  • 相关阅读:
    隐马尔科夫模型
    cmake modules default path
    opencv mat
    cmake 查找头文件和库文件顺序
    opencv3.0 legacy和nonfree丢失
    qt include_directories没用
    ros中删除某个包之后用apt安装的包找不到
    sql-select
    关于ros stage与navigation仿真总结5月16号
    关系型数据库
  • 原文地址:https://www.cnblogs.com/dlbrant/p/3100489.html
Copyright © 2011-2022 走看看