写程序,其实很多的时候都是在跟字符串打交道,因此,真正理解Delphi的字符串就很重要。一直以来,对于Delphi的字符串,我总是尝试着用,反正错了,我再尝试另外一种写法,这样一来,写的程序很多的时候心里都没有底!现在静下心来总结一下delph的字符串。
1. String之AnsiString、ShortString。
String这个类型是Delphi的亮点,也是很多时候容易出错的危险点。之所以容易出错,是因为string的形态与其相应的管理方式有关。
string可以是AnsiString,ShortString,这个跟使用方式、编译开关有关{$H}.Delphi里面默认的情况是AnsiString。
比如:
var
AnAnsiStr: string;
在默认的情况下,这个AnAnsiStr就是AnsiString。AnsiString顾名思义就是有AnsiChar组成的字符串。这就意味着,如果你用AnsiString表示一个汉字。
var
AnAnsiStr: string;
AnAnsiStr := '汉字';
由于汉字是占用两个字节的,因此,AnAnsiStr在内存中需要用四个字符表示“汉字”,两个字符表示了一个汉字,如果你要取出“字”这个字的话,采用AnAnsiStr[Length[AnAnsiStr]]只是试图取出“字”在内存中第二字节,因此无法得出“字”这个字,但是我们可以使用StrUtils中LastChar这个函数取出“字”。当然,操作双字节字符串,采用WideString是合适的选择。
1.1 AnsiString的一些特性
var
s: Ansistring;
那么这个s其实是一个指向一个存储AnssiChar的动态数组,这个动态数组的下标是从1开始的,这个动态数组的末尾永远会自动增加一个#0,有了这个#0,从内存结构上就跟PChar很相似了,所以一个AnsiString转换为PChar其实是很容易的。所以,我们可以经常这样运算:
var
s: string;
p: pchar;
s := 'This a string';
p := s;
由于一个AnsiString字符串变量其实就是一个指向动态数组的指针,动态数组从1开始,所以,要取出一个AnsiString中的第一个元素就是类似s[1].
扩展思考一下,s[0]是什么东西呢?其实对于AnsiString是不存在这个的,如果你尝试这样写,那么编译器是不会答应的。因为s:string中s就是一个指针,而后面的字符串内内容就是存放在一个动态数组里面,数组的下标是从1开始的,这是s[1]就是其首元素,没有s[0].
另外,AnsiString是自管理类型的,具体实现这个的是引用计数、字符串长度、Copy-On-Write共同实现的。这里简要说一下,对于一个AnsiString变量就是字符串的指针,那么这个指针的负偏移一个DWORD长度存储的是字符串的长度,负偏移2个DWORD长度处存储的引用计数,不过这个只能说是Delphi目前的一个约定,后面Delphi有可能更改。对于Copy-On-Write,很多书上都有说明,这倒不是一个很难理解的东西:只有修改了才真正的Copy,当然这个机制的存在也可能导致内存错误,原因是,如果一个字符串的引用数变成0了,系统就会回收资源,多次赋值操作的时候需要考虑到一点。
1.2 ShortString
如果关闭{$ H-},那么string类型就是一个ShortString。ShortString很简单,固定占用内存256个字节,首字节存储字符串的实际长度,因为有长度限制,所以字符串末尾不需要用#0标志结束。
2.2 定长字符串
定长的字符串最多定义为s : string[255],此时就等同于ShortString了。其余的长度值跟ShortString的管理类似。