zhouqijie

第四部分:字符串

一、字符串使用

在C/C++中字符串不是一个基本数据类型,而是体现为字符数组。

字符串的使用要考虑储存管理方式、操作开销、本地化等问题。

二、字符串类

STL提供了字符串类的实现。如果不使用STL的话可以自己实现字符串类。

使用字符串类之前必须了解它的性能开销。比如字符缓冲区是不是只读的,是不是使用了copy-on-write优化,是以引用传递还是以值传递。

字符串复制通常比int或者float等复制慢,因为其长度可变。strcmp和strcpy等操作都要检测\0空字符。

有一种情况是推荐使用字符串类的,那就是路径Path类。因为可以很方便地提取文件名、文件扩展名、目录,而且还方便跨平台。

三、唯一标识符

游戏对象需要某种唯一标识方法,游戏资源也需要唯一标识符,方便引擎寻找和操控。

可以使用字符串做标识符。游戏资产用文件路径作为标识符。游戏对象通常用一些清晰明了的名字做标识符。
比较标识符的速度必须很快,strcmp()是完全达不到要求的。可以使用字符串散列标识符。

字符串散列标识符:

字符串散列把字符串映射成整数,把实际字符串储存至散列表。

散列码能像整数一样比较。操作很迅速。
可以通过散列码取回原来的字符串。(一般用于debug)
这种散列字符串,虚幻引擎称为name。
有发生碰撞的几率,但是极小。(32位散列码能表示40亿个值)

实现方法:

大多数游戏引擎会在运行时进行散列。

也可以利用简单工具在预处理中散列字符串以便在例如switch-case等要求编译时常量的情况下使用字符串标识符。

散列字符串并加进全局字符串表的过程称为字符串扣留(string interning)。字符串扣留的速度缓慢,最好只把字符串扣留一次,然后存储备用。

虚幻引擎把字符串标识符和相应的C风格字符串封装成一个类。(名为FName)

如果把字符串放在调试内存中,最终发行版代码应该永不依赖那些字符串。

四、字符编码

ASCII:

美国信息交换标准代码。基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。只有128个字符。

MBCS(多字节字符集):

一些使用多于1字节的字符集,包括GBK、Big5等,通常不能实现多国文字混合使用。

Unicode:

Unicode的目标是一套字符集包揽各种文字。其编码格式(UTF-8/UTF-16)可以容许一个字符串内包含多国字符。

Unicode只是从数字和字符之间的逻辑映射的概念编码。并不是存储/传输格式编码。

UTF-32:

UTF-32是固定长度的编码(4个字节)。 UTF-32足以容纳所有Unicode字符。所以直接存储Unicode编号即可。
UTF-32缺点是占用较多空间。

UTF-16:

Unicode编码0-FFFF之间的字符,UTF-16使用2字节存储。(和UTF-32一样直接存储Unicode编码)
Unicode编码10000-10FFFF的字符使用4个字节存储。

xxxxxxxx xxxxxxxx
110110xx xxxxxxxx 110111xx xxxxxxxx

UTF-16大部分字符都以固定长度的字节(2字节)储存,编码很简单。

UTF-8:

UTF-8是Unicode的另一种可变长度字符编码实现。UTF8编码中每个字符占1-3字节。

0xxxxxxx
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-8向下兼容ASCII编码,单字节UTF8编码等同ASCII编码。所以ASCII字符串都是合法的UTF8字符串。

WINDOWS的Unicode编码:

在Windows下宽字符(WCS)UTF-16一般用wchar_t数据类型表示,而char一般用于表示ANSI字符。

ANSI WCS MBCS
strcmp() wcscmp() _mbscmp()
strcpy() wcscpy() _mbscpy()
strlen() wcslen() _mbslen()