Delphi编程基础 之 字符串篇

来源:互联网 发布:淘宝店铺名片怎么改 编辑:程序博客网 时间:2024/04/30 17:17

本文整理了 Delphi字符串格式 ShortString AnsiStringWideString。以及PCharChar数组的使用方法及需要注意的地方,很多例子来自网络。

本文所有例子基于Delphi7,使用例子如有不对之处,欢迎指正。

参考文献:delphi7 开发指南;

() ShortString

ShortString 的存储结构如下:

+---------------------+

| 1Byte | 字符串内容 |

+---------------------+

0          1

其第一个字节为字符串的长度,所以ShortString 的长度不能超过255,其存储区为静态分配,大小在编译时确定,这是继承于BP for Dos 的类型。保留该类型是为了向后兼容Delphi 1.0,和为了与Pascal 传统string 相兼容而保留的。但是这个字串数组的最后是没有一个#0 字符作为结尾的,因此ShortString C 语言的字符串是不兼容的。

()AnsiString

Delphi2.0 以后引入了AnsiString,,它是一个容易使用而且没有2 5 5 个字符限制的字符串类型。

而且它并不需要以NULL 结尾,因为有长度信息,同样因为这个字符串内可以包含任意的NULL 而不会被认为是字符串结尾。并且AnsiString n u l l 结束的字符串相兼容。其存储结构如下:

+-----------------------------------+

| 4B | 4B | 4B | 字符串内容 |

+----------------------------------+

-12   -8     -4   0 ... ...

内存偏移-12 处为该字符串分配的内存大小、内存偏移-8 处为该字符串的引用计数,内

存偏移-4 处为该字符串长度。AnsiString 字符串最大长度2G

AnsiString Delphi 独有的,其存储区在运行时动态分配的并有自动回收功能,因为这个功能A ns i S t r i n g 有时被称为生存期自管理类型。

当两个或更多的A n s i S t r i n g 类型共享一个指向相同物理地址的引用时,Delphi 使用

Copy – On - Wr i t e 的技术,即一个字符串要等到修改结束,才释放一个引用并分配一个物理字符串。下面的例子有助于理解这些概念:

我们平常使用的String 类型,在默认的情况下就是AnsiString;我们可以通过Huge Strings 的编译选项来将String 类型定义为ShortString,即将编译选项$H 设为负值。在缺省情况下编译选项$H 为正值,即String 类型为AnsiString

在使用AnsiString 是要注意以下几点:

一、$H 有一个例外。

使用$ H 规则的一个例外是,如果在定义时特地指定了长度(最大在2 5 5 个字符内),那么总是S ho r t S t r i n g

 

 

  

 

二、免重复初始化。

由于AnsiString 会自动初始化为空。如下代码中的初始化就纯属多余了。

 

s:='';就完全没有必要。但是值得注意的是这对函数返回值Result 无效。而一般说来,用var 实参传递比返回字符串值要更快一些。

 

三、用SetLength 预分配长字符串(AnsiString)。

虽然动态分配内存是AnsiString 的一大长项,但有时候反而会降低效率,还是要用到SetLength 来为AnsiString 类型的字符串来分配内存。请看如下例子。

当然此例可用Delete 取代之,这里主要说明在上例的循环中s2 的内存区域被不停地重复分配,降低效率。一个简单有效的办法如下:

这样s2 内存只会重新分配一次。

 

四、量避免使用ShortString

因为再Delphi 中很多字符串操作会先把ShortString 转换为AnsiString,从而减慢了执行速度,因此还是少使用短字符串为妙。

 

五、避免使用Copy 函数

这也和滥用内存管理有关。一个典型的情形如下:

if Copy(s1,23,64)=Copy(s2,15,64) then ……

这样导致分配了两块临时内存,因而降低了效率。应当替换为如下代码:

同样的,如下语句就显得相当低效:

s:=Copy(s,1,length(s)-10);

应改为

Delete(s,length(s)-10,10);

 

六、尽量使用AnsiString,必要时转换为Pchar

先看看AnsiString 的定义:

其中Astring[1]将返回Astring.ChrArr[1]的内容。

很多人认为AnsiString 是天生低效的。其实这在很大程度上是由代码编写不良、内存管理乱用和缺乏支持的函数所致。如上所述,一旦被动态分配了一块内存,长字符串就成了一个线性的字节序列,并无所谓的效率问题。当然,若有更多有效的函数支持那就更好了。

说到AnsiString PChar 的转换,本质上有三个办法:

(1) P:=@s[1];这会引发UniqueString 调用,UniqueString 使字符串拥有唯一的存储区域。

(2) P:=PChar (s);这会先检查s 是否为空,若是,则返回nil,否则即返回s[1]的地址。

(3) P:=Pointer(s);这不会引发任何隐含调用,因而是在确定s 非空情况下的最佳选择。

()Pchar

Pchar 就是纯指向字符串(#0 字符结尾)的指针,与C 语言中的char *是一样的。定义时由Delphi

自动填0PChar 主要是为了兼容各类API,在Delphi 中更加广泛应用,其存储区可以用字符数组静态分配,也可用GetMem 手动分配。

()Char 数组

Char 数组也是指向字符串的指针,它与Pchar 的区别在于:

1Char 数组(均指非动态数组)一旦定义好,它的长度就固定了;

2Char 数组的地址是常量,不能另赋其它值,不能像Pchar 一样

()WideString

WideString 长字符串类型与ANSIString 类型相似,只是它基于WideChar 字符类型,WideChar

字符为双字节Unicode 字符。但是Delphi vcl 是使用AnsiString 实现的,所以使用WideString Delphi 中实际使用会很不方便,这里不详细介绍了。

()一些例程

下面举一些具体的例子,有助于帮助我们理解Object Pascal 关于字符串的一些基础概念。

最好先不要用Delphi 调试。看一下个过程中ShowMessage()中的返回值。

 

//1

分析:答案为PChar(s)[0] := 'm';引起内存访问冲突。因为对于像 s1:='abcde' 或者

ShowMessage('wxyz') 这样的语句,常数字符串是放在代码段(CODE Segment)中的,在32 位保护模式下代码段是不能直接写入的。

 

//2

 

分析:答案为mbcde。代码和1 题差不多,但是s[1] := 'v';引起了字符串的Copy-On-Write 的机制为S分配了新的空间,所以可以安全的运行。

 

 

//3

分析:答案为mbcde。由于字符串的Copy-On-Write 的机制,字符串刚赋值后,实际上是指向同一块内存只是增加了引用计数,所以用这种方式修改字符串2,也就是修改了字符串1

 

//4

分析:答案为26AnsiString 的定义可以知道,AnsiString 并不需要以NULL 结尾,因为有长度信息,同样AnsiString 内可以包含任意的NULL 而不会被认为是字符串结尾。

 

//5

分析:答案20。注意,这种操作在编程中和4 题的结果是不同的。Pchar 是以“#0”作为字符结尾的。

 

//6

分析:答案为hello world.15。这里要注意的是:字符串赋值时复制的字符数只和字符串的长度有关,同时注意ShortString AnsiString 0 位置元素上的区别,ShortString 的实际长度由0 位置的字符决定。

 

//7

分析:答案为zabcdefghijklm:714。这里要注意字符串在传递给无类型可变参数时的区别,

ShortString 变量的地址实际表示0 元素的地址,传递给无类型可变参数时应写为S[1], AnsiString是一个指针,指向一个字符串变量的数据区,传递给无类型参数时,应为PChar(s)^ 或者 s[1]

 

//8

分析:答案为BMMMMMMMMM,后面可能会有不可预料的内容出现,因为没有#0 结尾,同时注意Move(s1, s2[0], 10); s1 是短字符串,引用s1 会发生内存溢出,修改s3 也相当于修改s2

 

原创粉丝点击