Delphi中有多种字符串类型,常见的有
UnicodeString
AnsiString
ShortString
不同类型的字符串可以在赋值语句和表达式中混用,编译器会自动进行类型转换。但是在某些情况下可能会造成数据的丢失。(例如双字节字符转换成单字节字符的时候。)
一、Unicodestring
在目前的Delphi中,String类型等价于UnicodeString类型。
UnicodeString的长度只受到可用内存的限制。它使用UTF-16编码,所以绝大多数字符都是两个字节长度。少数特殊符号需要用到两个UTF-16编码来表示,这种表示方法称为代理对(Surrogate Pair)。
UnicodeString类型的变量本质上是一个指针,它指向一个结构体。这个结构体中包含以下信息:字符串,字符串的长度,引用次数,每个字符所占的字节数以及代码页。若把字符串指针所指的地址看作原点,则上面这些信息所在的内存地址为:
距离原点的偏移量 |
-12 |
-10 |
-8 |
-4 |
0 |
长度*单个字符的字节数 |
内容 |
代码页 |
单个字符所占的字节数 |
引用次数 |
字符串长度 |
第一个字符 |
结束字符(NULL) |
因为UnicodeString类型的变量是一个指针,所以两个相同的字符串只需要指向同一块内存即可。当一个UnicodeString类型的变量被赋予一个新的值的时候,原先所指向的字符串的引用数减一,新字符串的引用数加一。若一个字符串的引用数为0,则这个字符串占用的内存被自动释放。这个操作被称为“引用计数”。
由于引用计数,字符串之间的赋值或者把字符串当做参数传递时,只会改变引用计数,没有任何数据被复制。假如有至少两个变量引用了同一块字符串数据,当其中一个变量试图改变字符串的内容时,系统会自动把这块字符串复制到新的内存中再进行更改,原内存区域引用数减一,其余内容不变。这被称为“写入时复制”技术。
二、AnsiString
AnsiString和UnicodeString的数据在内存中的组织结构完全一样。所以AnsiString也支持引用计数,写入时复制技术,它的长度也只受可用内存大小限制。
不同之处是,AnsiString是一种单字节字符串,而UnicodeString使用UTF-16编码,是双字节字符串。对于一个AnsiString类型的变量S来说,S[i]并不是第i个字符,而是第i个字节;Length(S)所显示的长度也不是字符串中的字符数,而是字节数。
举个例子
var
S: AnsiString;
begin
S:= '一只'+'abc'+'边牧';
Writeln(Length(S)); {显示11,因为一个中文占两个字节}
Writeln(S[6]); {显示b}
Readln;
end.
三、ShortString
ShortString主要是为了和早期Pascal的String类型兼容。在系统内部定义中
type ShortString = String[255];
我们也可以自己定义
type TMyStr = String[Len] {其中,0<Len<=255}
注意,String后面加了中括号就不再是String类型而是ShortString了。
不同于UnicodeString和AnsiString,ShortString类型的变量不是指针而是字符串的实际内容。不论字符串长度有多少,它占用的内存固定为Len+1。这个Len并不是字符串的长度,而是定义类型时候 type TMyStr = String[Len]的Len。所以默认的ShortString变量占用256个字节。其中,首字节(编号为0)记录着字符串的长度,后面的字节记录着字符串的内容。如果赋给ShortString变量的值超过了它的最大长度,超出部分会被截去。
除此以外,还有WideString, UTF8String, RawByteString等,就不再一一介绍了。