zoukankan      html  css  js  c++  java
  • C++字符串之一(字符表示)

    在C++中有两种类型可以用于表示字符,char和wchar_t。

    但是字符串格式的标准却有很多种,如ASCII,UTF8,UTF16,UTF32等等。字符串的格式和char/wchar_t 的关系是什么呢?

    首先要理解Unicode和UTF-8的关系,可以参考我转帖的文章:http://www.cnblogs.com/whyandinside/archive/2012/02/05/2338841.html 

    额外还有几个问题需要解决:

    1. UCS-2、UCS-4、BMP

    UCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。下面让我们做一些简单的数学游戏:

      UCS-2有2^16=65536个码位,UCS-4有2^31=2147483648个码位。

      UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为256行 (rows),每行包含256个cells。当然同一行的cells只是最后一个字节不同,其余都相同。

      group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP。

    UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。

     

    2. UTF-16是如何表示Unicode的?

    在基本多語言平面內定義的符號((Basic Multilingual Plane, BMP),或稱第零平面(Plane 0)),使用2個字節表示,在此之外的字符(其他平面內的字符),則使用4個字節表示。由於第零平面內,從0XD800到0XDFFF之間的區段是沒有使用的,因此可以利用0XD800-0XDFFF之間的值來對輔助平面的字符進行編碼。 
    其編碼方法是:

    1. 如果字符編碼U小於0x10000,也就是十進制的0到65535之內,則直接使用兩字節表示;
    2. 如果字符編碼U大於0x10000,由於UNICODE編碼範圍最大為0x10FFFF,從0x10000到0x10FFFF之間 共有0xFFFFF個編碼,也就是需要20個bit就可以標示這些編碼。用U'表示從0-0xFFFFF之間的值,將其前 10 bit作為高位和16 bit的數值0xD800進行 邏輯or 操作,將後10 bit作為低位和0xDC00做 邏輯or 操作,這樣組成的 4個byte就構成了U的編碼。

    UTF-16可看成是UCS-2的父集。在沒有輔助平面字符Mapping of Unicode character planes(surrogate code points)前,UTF-16與UCS-2所指的是同一的意思。但當引入輔助平面字符後,就稱為UTF-16了。現在若有軟件聲稱自己支援UCS-2編碼,那其實是暗指它不能支援在UTF-16中超過2bytes的字集。對於小於0x10000的UCS碼,UTF-16編碼就等於UCS碼。

     

    3. wchar_t是UTF-16吗?他们之间的关系是什么?

    wchar_t在不同的平台上size是不一样的,MSVC它是2字节的;而在Linux上它就是4字节了。wchar_t在Windows可用于存储UTF-16的字符;而在Linux上往往用于存储UTF-32在字符。

    比如:Windows上的函数MultiByteToWideChar是这样描述的:Maps a character string to a UTF-16 (wide character) string. The character string is not necessarily from a multibyte character set.

    int MultiByteToWideChar( __in UINT CodePage, __in DWORD dwFlags, __in LPCSTR lpMultiByteStr, __in int cbMultiByte, __out LPWSTR lpWideCharStr, __in int cchWideChar );

    Different compilers and platforms define wchar_t differently, because they use different Unicode encoding techniques. On Windows/Visual C++ for instance, wchar_t is a 16 bit type, suitable for UTF-16. On GCC/Linux for instance, wchar_t is a 32 bit type, suitable for UTF-32.

    Depending on your requirements (speed, memory usage), you should pick an internal format that suits the platform. On Windows it might be UTF-16, and on Linux it might be UTF-32. That way you won't have to transcode strings all the time, just to make simple platform-defined operations on them (wcslen(), wcscmp(), etc).

    For external formats (text files, etc), I tend to use UTF-8. The reason is that files are considerably smaller if they contain text in a western language. Another benefit is that you don't have to consider endianess in UTF-8, which makes the chance of errors (on your or some other's part) less likely.

    4. SBCS、DBCS和MBCS 
    SBCS、DBCS和MBCS分别是单字节字符集、双字节字符集和多字节字符集的缩写。SBCS、DBCS和MBCS的最大编码长度分别是1字节、两字节和大于两字节(例如4或5字节)。例如:代码页1252 (ANSI-拉丁文 I)是单字节字符集;代码页936 (ANSI/OEM-简体中文 GBK)是双字节字符集;代码页54936 (GB18030 简体中文)是多字节字符集。单字节字符集中的字符都用一个字节表示。显然,SBCS最多只能容纳256个字符。

    双字节字符集的字符用一个或两个字节表示。那么我们从文本数据中读到一个字节时,怎么判断它是单字节字符,还是双字节字符的首字符?答案是通过字节所处范围来判断。例如:在GBK编码中,单字节字符的范围是0x00-0x80,双字节字符首字节的范围是0x81到0xFE。我们顺序读取字节数据,如果读到的字节在0x81到0xFE内,那么这个字节就是双字节字符的首字节。GBK定义双字节字符的尾字节范围是0x40到0x7E和0x80到0xFE。

    GB18030是多字节字符集,它的字符可以用一个、两个或四个字节表示。这时我们又如何判断一个字节是属于单字节字符,双字节字符,还是四字节字符?GB18030与GBK是兼容的,它利用了GBK双字节字符尾字节的未使用码位。GB18030的四字节字符的第一字节的范围也是0x81到0xFE,第二字节的范围是0x30-0x39。通过第二字节所处范围就可以区分双字节字符和四字节字符。GB18030定义四字节字符的第三字节范围是0x81到0xFE,第四字节范围是0x30-0x39。

     

    5. UTF-8 可以和ANSII兼容;而UTF-16和UTF-32不可以。

    6. 字符串有两种存储方式:保存总长度和结束符。结束符是最常使用的,比如char字符串使用来判断字符串结尾;而wchar的字符串使用’’来判断字符串结尾。 保存总长度也是一种方式,在COM中BSTR就是这样使用的。

    BSTR(Basic STRing)实际上是类Pascal字符串,在BSTR的前4个字节保存了字符串长度,所以BSTR内部可以包括,它也不是靠来判断字符串结尾的。BSTR在c/c++中的定义实际上是wchar_t*,但因为BSTR是依靠前4个字节来判断字符串长度的,所以它的分配和释放都不同于普通的字符串,需要通过::SysAllocString和::SysFreeString等函数来分配和释放。

     

     

    参考:

    1. http://blog.sina.com.cn/s/blog_4c860d7e0100hzzj.html

  • 相关阅读:
    解决Nginx不支持pathinfo的问题
    PHP获取当前服务器信息的基本语句
    权重结构的加权排序算法
    《深入探讨C++对象模型》笔记 二
    链表的一些常用操作
    invalidate作用
    GetMessage()和PeekMessage()区别
    C语言程序编译的内存分配
    assert() 宏用法
    开始写博客
  • 原文地址:https://www.cnblogs.com/lidabo/p/3325606.html
Copyright © 2011-2022 走看看