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字符串使用\0来判断字符串结尾;而wchar的字符串使用’\0\0’来判断字符串结尾。 保存总长度也是一种方式,在COM中BSTR就是这样使用的。

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

 

 

参考:

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

时间: 2025-01-25 09:54:06

C++字符串之一(字符表示)的相关文章

Swift语法专题四——字符串与字符

Swift解读专题四--字符串与字符 一.引言         Swift中提供了String类型与Characters类型来处理字符串和字符数据,Swift中的String类型除了提供了许多方便开发者使用的方法外,还可以与Foundation框架的NSString类进行转换,使用起来十分方便. 二.String基础         在Swift中,使用双引号来定义字符串,开发者可以通过如下代码来创建一个字符串常量: let str = "Hello, playground" 可以通过

输入一个字符串并打印出该字符串中字符的所有排列

题目: 输入一个字符串, 打印出该字符串中字符的所有排列. 方法: 使用递归依次交换位置, 打印输出. 代码: /* * main.cpp * * Created on: 2014.6.12 * Author: Spike */ /*eclipse cdt, gcc 4.8.1*/ #include <stdio.h> void Permutation(char* str, char* begin); void Permutation(char* str){ if(str == NULL) r

php替换字符串中间字符为省略号的方法

  本文实例讲述了php替换字符串中间字符为省略号的方法.分享给大家供大家参考.具体分析如下: 对于一个长字符串,如果你只希望用户看到头尾的部分内容,隐藏掉中间内容,你可以使用这个php函数,他可以指定要隐藏掉的中间字符串的数量 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 /**

php去除字符串中空字符的常用方法小结

 这篇文章主要介绍了php去除字符串中空字符的常用方法,实例分析了php中的trim().ltrim().rtrim()及chop()等函数的使用技巧,非常具有实用价值,需要的朋友可以参考下     本文实例总结了php去除字符串中空字符的常用方法.分享给大家供大家参考.具体分析如下: php中包含四个可以去除字符串空格的函数: trim() – 去除字符串两端的空字符 ltrim() – 去除字符串前端的空字符 rtrim() – 去除字符串末尾的空字符 chop() –同rtrim(). 代

java类的问题-java题,将该字符串每个字符与&amp;amp;#39;U&amp;amp;#39;之差的绝对值的升序排序

问题描述 java题,将该字符串每个字符与'U'之差的绝对值的升序排序 输入一组字符串,将该字符串每个字符与'U'之差的绝对值的升序排序.在线等!!? 解决方案 每个字符都减去同一个字符跟不减不一样么?没有必要啊.直接排序即可. 解决方案二: 首先,按照我的一贯观点,这种问题和编程语言无关,和编程语言无关,和编程语言无关.重要的事情说三遍! 设计重点:1.与U的差值存储;2.升序排序. 已知的排序算法相当多,不过在本问题情形下,需要稍作变通. 给出一个简单方案吧:给定字符串Char[];比较差值

《Python语言程序设计》——3.3 字符串和字符

3.3 字符串和字符 关键点:字符串(第1章讲过)是一连串的字符.Python处理字符和字符串的方式是一样的. 在Python里,你除了可以处理数值,还可以处理字符串.一个字符串就是一串包括文本和数字的字符.字符串必须被括在一对单引号( ' )或者双引号( " )里.Python没有字符数据类型.一个字符的字符串代表一个字符.例如: 第一条语句将只有字符A的字符串赋值给变量letter.第二条语句将只有数字字符4的字符串赋值给变量number.第三条语句将字符串"good mornin

编码-霍夫曼树程序,输入字符串统计字符出现次数并译码。请问如何改成从文件读入字符串?

问题描述 霍夫曼树程序,输入字符串统计字符出现次数并译码.请问如何改成从文件读入字符串? //生成HuffmanCode文件的两个函数void HuffmanEncoding(HuffmanTree HTHuffmanCode HC){//根据HuffmanTreeHT求HuffmanCode表HC int cpi; char cd[n]; int start; cd[num] = ''; for(i = 1;i <= num;i++){ start = num; c = i; while((p

Swift语言指南(十)--字符串与字符

原文:Swift语言指南(十)--字符串与字符 字符串是一段字符的有序集合,如"hellow,world"或"信天翁".Swift 中的字符串由 String 类型表示,对应着 Character 类型值的集合. Swift 中的 String 类型为你的编程提供了一个高速的,兼容 Unicode规范 的文本处理方式.Swift 创建和处理字符串的语法轻量可读,与 C 语言的字符串语法颇为相似.字符串的拼接非常简单,只需将两个字符串用 + 运算符相加.字符串的值是否

Swift学习之十:字符串与字符(Strings and Characters)

字符串是有序的字符集,如"hello, world" 或者 "albaatross".在Swift中字符串类型是String,由有序的Character组合而成. Swift中的String与Character类型提供了快速,与Unicode兼容的方式在代码中操作文本.String字符串的创建与操作的语法 是轻量的,可阅读的,与C语言中的字符串语法类似.字符串的连接可以使用+操作符直接相连,字符串可变性是通过选择 常量或者变量,就像Swift中的其它任何类型一样.

string-android,替换字符串中*字符

问题描述 android,替换字符串中*字符 有一个字符串变量,包含*.但是我需要替换其中所以的*字符. 我写的代码没有实现: text = text.replaceAll("*",""); text = text.replaceAll("*",null); 请大家帮忙解决,谢谢. 解决方案 用String#replace() 方法实现多简单,这个方法不会将regex当做参数. text = text.replace("*",