Delphi 中的字符串
——《Delphi6 开发人员指南》读书笔记
Spacesoft【暗夜狂沙】
Delphi 对字符串这个结构的支持是十分丰富的,不仅有Delphi 本身支持的string 类型,还支持和C 语言兼容的字符串数组。那么他们之间有什么区别呢?本文试图就此做一个详细的剖析,并且试图回答论坛上常见的几个问题。
首先,我们要讨论的是Delphi 中的string 到底是什么东东,因为我们在OP 的语法参考手册上知道,Delphi 有三种string : ShortString、AnsiString (就是兼容BCB 的AnsiString 的那种)以及WideString (用来支持Unicode 的,其他的和AnsiString 是完全一样的)。在默认的情况下,编译器将把string 解释为AnsiString ,而需要的时候,可以设置使编译器将string 解释为ShortString(当然,我们很少见到人要这么做,因为AnsiString 用得很好,而ShortString 只支持255 个字符,并且和C 语言的字串数组不兼容,这个我们后面会提到),对应的编译器指示符号是默认的{$H+} 和 {$H-}。
那么,这三种string 到底有什么差别呢?差别在于他们的不同结构。
对于ShortString 来说,它在内存中的结构可以表示为:
strShort = record
wLength: WORD;
szBuf: array of Char;
end;
也就是说,对ShortString 的每次操作,不需要遍历这个字符串便可以取得这个字串的大小了。另外,这个字串数组的最后是没有一个#0 字符作为结尾的。因此ShortString 的数组和C 语言的字符串是不兼容的。
对于AnsiString 来说,根据《Delphi6 开发人员指南》的描述,它的结构可以表示如下:
strAnsi = record
nSize: Integer;
nRef: Integer;
nLength: Integer;
szBuf: array of Char;
end;
也就是说AnsiString 不仅仅保存了本字串的长度,还保存了对这个字符串的引用数。因此,不同的AnsiString 事实上可能具有相同的物理地址,所以Delphi 的字符串拷贝经常是效率惊人的。然而,当一个字符串改变时怎么办呢?Delphi 将释放对这个字符串的引用(把nRefCount 减1),然后新建一个字符串来装载新的内容。
另外,与ShortString 不同的是,AnsiString 的字符数组是以#0 结尾的,这样szBuf 就可以被当成C 中的字符数组了。估计这样的设计是为了兼容Win32 API。
WideString 和AnsiString 基本上是一样的,所以我们讨论的时候,仅仅讨论AnsiString 和ShortString。
然后, 我们要说明的是,string 具有生存期管理特性。就是说,当string 超出作用域后,字符串占用的资源自动被释放掉。这个机制是怎么实现的呢?对于全局变量,当然它的作用域就是它所在的Unit 的生存周期,那么当然可以在finalization 段进行释放了。而函数中的变量呢?编译器就自动在整个函数的外面套上一个try ... finally 处理。这样,不管什么情况下,这些变量就总能释放掉了。
下面一个问题是:假如现在有一个指针,它被指向一个string, 那么它指向的东西是什么呢?
答案是:指向szBuf 的开头,并且ShortString 不支持这样的强制转换(这是很自然的,因为做这样转化的目的纯粹就是为了和C 语言兼容)。于是string[1] 就是这个字符串的第一个字符。于是,“看起来”这个字符串就只有那些字符了。于是,我们就可以用PChar() 强制类型转化把一个string 转化为一个PChar,然后传到需要字符数组作为参数的Win32 API 中,因为“看起来” 这个字符串这个时候确实是一个标准的C 语言字符串。甚至,我们可以这样使用字符串:
procedure Test();
var
strBuf: string;
begin
SetLength(strBuf, 255); //千万不要忘了先给你的字符串申请足够的空间,不然……嘿嘿,等着弹框儿吧^_^
GetModuleFileName(0, PChar(strBuf), 255);
ShowMessage(strBuf);
end;
我们知道,这个API 的原型是这样的:
DWORD GetModuleFileName(
HMODULE hModule, // handle to module
LPTSTR lpFilename, // Pointer to a buffer that receives the fully-qualified path for the module
DWORD nSize // size of buffer
);
这个API居然以为我们真的根据它的要求传了一个array of Char 进去!
通过类似这样的机制,Delphi 使它的使用者在C 语言写的API 原型中可以自由的穿行,一点都没有“二等公民”的感觉,而且又可以享用Delphi 本身的数据类型带来的方便,了不起!
现在我们来总结一下Delphi 中字串类型的特点:
1、与C 语言不同,不是依靠#0 字符做字串结束标志,而是通过字串前面的数字来记录字符串的长度(尽管为了和C 语言兼容,AnsiString 的字串实现确实是在结尾放了一个#0 字符的)
2、string 是具有生存期管理特性的,当string 超出作用域后,字符串占用的资源自动被释放掉。
3、对字符串进行强制类型转化为PChar ,会得到一个字符串指针,这个字串指针的用法和C 语言中的数组是一样的。
对于
strBuf: string;
SetLength(strBuf, 255);
你在使用时,可以把PChar(strBuf) 看作是C 语言里的这样一个东西:
char strBuf[255];
记住,在上个声明里strBuf 是一个指针,它指向那个指针数组的第一个字符的地址,而你做PChar(strBuf) 运算得来的指针就是这个东西。
在最后,解释一下《开发人员指南》里面引起过一段争论的一句话:
“在练习将一个字符串转换为PChar类型时要小心,因为字符串在超出其作用范围时有自动回收的功能,因此当进行P:=PChar(Str)的赋值时,P的作用域(生存期)要比Str 长。”
这句话的意思是这样的:字符串类型是具有自动回收功能的,但是字符串指针没有。P:=PChar(Str)返回的指针可能在作用域之外使用,因此P 的生存周期可能比Str 要长。举个例子来说明:
procedure getPChar(P: PChar);
var
strTmp: string;
begin
strTmp := 'asasass';
P := PChar(strTmp);
end;
这个时候,明显返回的这个指针P 的作用域要大于strTmp,那么这个过程结束的时候,strTmp已经被自动释放掉了。调用这个过程的函数得到的P 事实上已经是一个悬挂指针,没有意义了。作者的本意是提醒读者注意防止这样的情况发生。
参考文献:
1、《Delphi6 开发人员指南》,Steve Teixeira 等著,龙劲松等译,机械工业出版社,北京,2003年1月第一版
2、《Object Pascal 语言参考手册》
分享到:
相关推荐
在Delphi编程语言中,字符串处理是极其常见的操作,尤其是在数据处理、文本分析和用户界面设计等场景下。Delphi提供了丰富的字符串处理函数,其中包括了截取、拼接、删除、插入等功能,极大地简化了开发者的编码工作...
在Delphi编程环境中,处理中文和英文混合的字符串截取是一项常见的任务,特别是在涉及到文本处理、数据解析或者用户界面展示时。由于Unicode编码的存在,中文字符通常占据两个字节,而英文字符则占据一个字节,这就...
在Delphi中处理字符串,我们可以利用其内置的字符串类型(String)以及丰富的字符串函数和方法。 字符串相似度计算通常用于找出两个字符串之间的相似程度,这在许多场景下都非常有用,例如拼写检查、搜索引擎排名、...
Delphi中处理字符串的相关方法 1、字符集转换方法 (1)stringtowidechar function stringtowidechar(const source:string;dest :pwidechar;destsize :integer) :pwidechar; 将默认string类型的字符串转换为unicode...
在DELPHI中,我们可以利用各种压缩库或算法来实现字符串的压缩。例如,ZLib库提供了一套完整的压缩和解压缩功能,包括常用的Deflate算法。以下是一个简单的DELPHI使用ZLib压缩RTF字符串的例子: 首先,你需要引入...
本示例主要涉及如何在Delphi中处理gzip压缩的字符串,包括解压和压缩。 在描述中提到的"完整的库文件代码"可能是指`DelphiZLib.123`,这是一个很可能包含`ZLib`库的文件。`ZLib`是一个开源的压缩库,支持多种压缩...
在IT行业中,字符串压缩是一种常见的优化技术,尤其是在Delphi编程环境下。Delphi是Pascal语言的一个强大版本,它提供了一套完整的开发工具,用于创建高效、高性能的应用程序。字符串压缩的目的是减小数据的存储空间...
在 Delphi 编程语言中,处理字符串表达式的计算是一项常见的任务,尤其是在需要动态解析数学表达式的情况下。本文将深入探讨如何编写一个简单的数值字符表达式计算函数,该函数能够处理基本的数学运算(包括加、减、...
在本节中,我们将详细介绍 Delphi 字符串和数组函数的使用方法和示例代码。 1. Copy 函数 Copy 函数用于从字符串中复制指定范围中的字符。该函数有三个参数。第一个参数是数据源(即被复制的字符串),第二个参数...
以下将详细介绍如何在Delphi中进行枚举到字符串以及字符串到枚举的转换。 1. **枚举到字符串的转换** 在Delphi中,我们可以使用`Format`函数或者`IntToStr`配合枚举类型的`Ord`函数来将枚举值转换为字符串。`Ord`...
在Delphi中,16进制字符串转换为整数或字节数组,以及将整数或字节数组转换为16进制字符串,有多种方法。我们可以使用系统提供的函数,如`Hex`和`IntToHex`来进行转换。 1. **16进制字符串转整数**: - 使用`...
在Delphi编程中,判断一个字符串是否符合日期格式是一个常见的任务。这个任务通常涉及到字符串处理、日期时间操作以及条件判断。下面将详细解释这个`TF_tn.IsDate`函数实现的逻辑。 首先,函数接收一个名为`Var...
在Delphi编程环境中,字符串处理是一项基础且重要的任务。初学者常常会遇到如何在特定位置删除字符串中的某个部分。在Delphi7中,这可以通过多种方法实现,包括使用内置的字符串函数、正则表达式或者自定义的逻辑。...
Delphi TMemo字符串的查找完全实现 已经完全的解决Delphi TMemo的查找对话框 和 替换对话框 功能 的所有功能 查看 GIF http://images.cnblogs.com/cnblogs_com/xe2011/524919/o_TFindDialog.gif
在编程领域,字符串处理是一项基础且重要的任务,特别是在Delphi这样的面向对象的编程环境中。本文将深入探讨Delphi中的字符串分割技术,通过一个具体的示例源码来展示如何实现这一功能。 Delphi 是一个强大的...
在本示例中,我们看到的是用DELPHI实现的Levenshtein算法,这为DELPHI开发者提供了一种在自己的项目中评估字符串相似性的工具。 首先,`LevenshteinUnit.pas` 文件很可能是包含算法实现的主要源代码单元。在DELPHI...
delphi7和delphi2010直接因字符串编码方式不一样,不能直接调用,本例子是本人编写的ascii码和unico之间的转变。同时也可以解决很多delphi2010调用delphi7编写的动态库连接问题
总的来说,了解和掌握LCS算法在DELPHI中的实现,有助于你在文本分析和比较任务中有效地评估字符串的相似性。通过学习和实践这个示例,你可以进一步加深对动态规划的理解,并将其应用到其他领域,如生物信息学中的DNA...
在Delphi 7中,处理字符串是编程过程中常见的任务之一,特别是在处理用户输入、数据分析或者文件解析等场景。本文将详细讲解如何在Delphi 7中进行字符串分割,以满足不同需求。 首先,我们需要了解Delphi 7中的字符...
在Delphi编程环境中,字符串操作是常见的任务之一,其中替换字符串中的特定子串是一项基本功能。内置的`StringReplace`函数虽然提供了基本的替换服务,但在处理大量数据或长字符串时,其性能可能不尽人意。针对这种...