字符串在编程中是一个常见的类型,但左一个类型,右一个类型可能把许多人都弄迷糊了。我写这篇文章,试图给大家理清 Delphi / C++ Builder 中的字符串类型。关于这些字符串类型的历史,我们就不再啰嗦,历史就是历史,现在我们面对现实。
- String 类型
String 类型在 Delphi / C++ Builder 不同的版本中,对应于不同的类型,它们的分隔线是 Delphi / C++ Builder 2009。在 2009 以前,String 类型映射到了 AnsiString,而在 ≥ 2009 版本及以后,String 类型映射到了 UnicodeString。
- ShortString 类型
ShortString 是 Delphi 中的一个短字符串类型,最大长度为 255 个字符。它的编码方式和下面的 AnsiString 一致。
- AnsiString 类型
AnsiString 类型是一种基于引用计数的字符串类型,它实际是代码页为 CP_ACP (0) 的字符串类型。在传递参数时,直接增加的是字符串的引用计数,而不是复制字符串的内容,所以效率会比较高。英文数字和字符占用 1 个字节,中文占用 2 或 4 个字节,绝大部分中文占用的是 2 个字节。
- Utf8String 类型
Utf8String 在 2009 以前的版本,被直接映射到了 AnsiString 上。而在 2009 及以后,Utf8String 是代码页为 CP_UTF8(65001)编码的字符串类型。所以它也是一个基于引用计数的类型。英文数字和字符占用1个字节,中文一般占用 2 到 4 个字节,按 UTF8 规范最多占用 6 个字节。
- WideString 类型
WideString 是 Unicode 16 LE 编码的字符串,它是 COM 兼容的类型。当它做为一个参数传递时,它需要创建一份值拷贝,所以效率上要稍差。其中的每一个字符的类型都是 Delphi 中的 WideChar 或 C++ Builder 中的 wchar_t。对于 Unicode 16 LE 编码字符,我们需要注意扩展区字符,一个非扩展区字符占用 2 个字节,而扩展区的字符占用 4 个字节。扩展区字符的编码首个字符的编码范围是 0xD800 ~ 0xDBFF,第二个字符的编码范围是 0xDC00 ~ 0xDFFF。
- UnicodeString 类型
UnicodeString 是从 2009 开始引入的字符串类型,它也是基于引用计数的,所以效率上要比 WideString 快的多。我们可以认为它是 WideString 的引用计数版本,对应的也是Unicode 16 LE 编码的字符串。而且 2009 开始将 String 类型映射到 UnicodeString。它的编码方式和范围同 WideStrnig。
- RawString 类型
RawString 实际是一种新的字符串类型,它实际上是一种没有任何内部编码的字符串类型。它只是一个容器,内部的字符编码没有具体约定。
- std::string
这个是 C++ 里的字符串类型,用于支持非 Unicode 16 LE 字符串,实际上是 basic_string<char>。可以认为它是 Delphi 中 AnsiString 的 C++ 原生版本,不过要明白,它没有引用计数。
- std::wstring
这个是 C++ 里的字符串类型,用于支持 Unicode 16 LE 编码字符串,实际上是 basic_string<wchar_t>。可以认为它是 Delphi 中 WideString 的 C++ 原生版本。
- PAnsiChar / char *
Ansi 编码的字符串指针类型。AnsiString、Utf8String、std::string 的内容都可以转换成这种字符串指针类型。由于 ShortString 不是一种以 ASCII 码 0 为结束的字符串,不符合 C 语言中的字符串规则,所以,一般情况下,我们需要先将 ShortString赋值给 AnsiString 然后才能转换为这种指针类型。
- PWideChar / wchar_t *
Unicode 编码的字符串指针类型。WideString、UnicodeString、std::wstring 的内容都可以转换成这种字符串类型。
好了,前面说了那么多,那么这些字符串类型之间如何进行相互转换呢?
- AnsiString <-> Utf8String / WideString / UnicodeString 类型转换
直接赋值即可,不需要额外的处理,也不会出现乱码。反过来则由于可能字符集的支持问题,造成乱码。
- AnsiString -> PAnsiChar/char *
Delphi 中直接用 PAnsiChar(变量名) 即可,而 C++ 中使用 变量名.c_str() 函数来返回首个字符的地址。
- WideString / UnicodeString -> PWideChar / wchar_t *
Delphi 中直接用 PWideChar(变量名)即可,而 C++ 中使用 变量名.c_str() 或 变量名.c_bstr() 函数来返回首个字符的地址。
- ShortString -> PAnsiChar/char *
不好意思,此路不通。原因前面说了,所以赋值给一个 AnsiString 然后再按前面的方法转才是正道。
- std::string -> char *
STL 的标准方法 变量名.begin() 就是了。
- std::wstring -> wchar_t *
同上,STL里的 变量名.begin() 就是了。
反过来,我们将 PAnsiChar / char * 或 PWideChar / wchar_t * 类型赋值给其它类型时,直接赋值就可以了。
在这里我还要提醒一点,2009 以前版本的 Utf8String 实际上是 AnsiString 的别名,所以不要想当然的认为其中存贮的就是 UTF-8 编码的字符串。
另外,在 FMX 环境下,AnsiString、WideString和Utf8String 这三个类型默认都消失不见了,所以对应的 PAnsiChar 也就没有定义了。取而代之的是:MarshaledAString 等价于 PAnsiChar,而 MarshaledString 等价于 PWideChar,可以用在需要的场合。
如果有什么疑问或补充,我们回头在群里聊。