开发Delphi对象式数据管理功能 TStream流类

20.1.1.2 TStream的实现原理

 

  TStream对象是Stream对象的基础类,这是Stream对象的基础。为了能在不同媒介上的存储数据对象,后继的Stream对象主要是在ReadWrite方法上做了改进,。因此,了解TStream是掌握Stream对象管理的核心。Borland公司虽然提供了Stream对象的接口说明文档,但对于其实现和应用方法却没有提及,笔者是从Borland Delphi 2.0 Client/Server Suite 提供的源代码和部分例子程序中掌握了流式对象技术。

  下面就从TStream的属性和方法的实现开始。

  1. TStream属性的实现

  前面介绍过,TStream具有PositionSize两个属性,作为抽象数据类型,它抽象了在各种存储媒介中读写数据所需要经常访问的域。那么它们是怎样实现的呢?

  在自定义部件编写这一章中介绍过部件属性定义中的读写控制。PositionSize也作了读写控制。定义如下:

 

property Position: Longint read GetPosition write SetPosition;

property Size: Longint read GetSize;

 

  由上可知,Position是可读写属性,而Size是只读的。

  Position属性的实现就体现在GetPositionSetPosition。当在程序运行过程中,任何读取Position的值和给Position赋值的操作都会自动触发私有方法GetPositionSetPosition。两个方法的声明如下:

 

function TStream.GetPosition: Longint;

begin

Result := Seek(0, 1);

end;

 

procedure TStream.SetPosition(Pos: Longint);

begin

Seek(Pos, 0);

end;

 

在设置位置时,Delphi编译机制会自动将Position传为Pos

  前面介绍过Seek的使用方法,第一参数是移动偏移量,第二个参数是移动的起点,返回值是移动后的指针位置。

  Size属性的实现只有读控制,完全屏蔽了写操作。读控制方法GetSize实现如下:

 

function TStream.GetSize: Longint;

var

Pos: Longint;

begin

Pos := Seek(0, 1);

Result := Seek(0, 2);

Seek(Pos, 0);

end;

 

2. TStream方法的实现

  ⑴ CopyFrom方法

  CopyFromStream对象中很有用的方法,它用于在不同存储媒介中拷贝数据。例如,内存与外部文件之间、内存与数据库字段之间等。它简化了许多内存分配、文件打开和读写等的细节,将所有拷贝操作都统一到Stream对象上。

  前面曾介绍:CopyFrom方法带SourceCount两个参数并返回长整型。该方法将Count个字节的内容从Source拷贝到当前流中,如果Count值为0则拷贝所有数据。

 

function TStream.CopyFrom(Source: TStream; Count: Longint): Longint;

const

MaxBufSize = $F000;

var

BufSize, N: Integer;

Buffer: PChar;

begin

if Count = 0 then

begin

Source.Position := 0;

CouNG="ZH-CN">资源文件中的部件时调用,通常程序员不需自己调用。如果读取的不是资源文件ReadResHeader,将触发异常事件。

 

procedure TStream.ReadResHeader;

var

ReadCount: Longint;

Header: array[0..79] of Char;

begin

FillChar(Header, SizeOf(Header), 0);

ReadCount := Read(Header, SizeOf(Header) - 1);

if (Byte((@Header[0])^) = $FF) and (Word((@Header[1])^) = 10) then

Seek(StrLen(Header + 3) + 10 - ReadCount, 1)

else

raise EInvalidImage.CreateRes(SInvalidImage);

end;

 

  ReadComponentResWindows资源文件中读取部件,为了判断是否是资源文件,它首先调用ReadResHeader方法,然后调用ReadComponent方法读取Instance指定的部件。下面是它的实现:

 

function TStream.ReadComponentRes(Instance: TComponent): TComponent;

begin

ReadResHeader;

Result := ReadComponent(Instance);

end;

 

 与ReadComponentRes相应的写方法是WriteComponentResDelphi 调用这两个方法读写窗体文件(DFM文件),在后面书中会举用这两个方法读取DFM文件的例子。

  ⑷ WriteComponentWriteDescendant方法

  Stream对象的WriteDescendant方法在实现过程中,创建了TWriter对象,然后利用TWriterWriteDescendant方法将Instance写入流。而WriteComponent方法只是简单地调用WriteDescendant方法将Instance写入流。它们的实现如下:

  

procedure TStream.WriteComponent(Instance: TComponent);

begin

WriteDescendent(Instance, nil);

end;

 

procedure TStream.WriteDescendent(Instance, Ancestor: TComponent);

var

Writer: TWriter;

begin

Writer := TWriter.Create(Self, 4096);

try

Writer.WriteDescendent(Instance, Ancestor);

finally

Writer.Free;

end;

end;

 

  ⑸ WriteDescendantResWriteComponentRes方法

  WriteDescendantRes方法用于将部件写入Windows资源文件;而WriteComponentRes 方法只是简单地调用WriteDescendantRes方法,它们的实现如下:

 

procedure TStream.WriteComponentRes(const ResName: string; Instance:

TComponent);

begin

WriteDescendentRes(ResName, Instance, nil);

end;

 

procedure TStream.WriteDescendentRes(const ResName: string; Instance,

Ancestor: TComponent);

var

HeaderSize: Integer;

Origin, ImageSize: Longint;

Header: array[0..79] of Char;

begin

Byte((@Header[0])^) := $FF;

Word((@Header[1])^) := 10;

HeaderSize := StrLen(StrUpper(StrPLCopy(@Header[3], ResName, 63))) + 10;

Word((@Header[HeaderSize - 6])^) := $1030;

Longint((@Header[HeaderSize - 4])^) := 0;

WriteBuffer(Header, HeaderSize);

Origin := Position;

WriteDescendent(Instance, Ancestor);

ImageSize := Position - Origin;

Position := Origin - 4;

WriteBuffer(ImageSize, SizeOf(Longint));

Position := Origin + ImageSize;

end;

 

  WriteCompnentRes是与ReadComponentRes相应的对象写方法,这两个方法相互配合可读取DelphiDFM文件,从而利用Delphi系统的功能。

 

20.1.2 THandleStream对象

 

  THandleStream对象的行为特别象FileStream对象,所不同的是它通过已创建的文件句柄而不是文件名来存储流中的数据。

  THandleStream对象定义了Handle属性,该属性提供了对文件句柄的只读访问,并且Handle属性可以作为DelphiRTL文件管理函数的参数,利用文件类函数来读写数据。THandleStream覆盖了构造函数Create,该函数带有Handle 参数,该参数指定与THandleStream对象相关的文件句柄。

 

20.1.2.1 THandleStream的属性的方法:

 

  1. Handle属性

  声明:property Handle: Integer;

Handle属性提供了对文件句柄的只读访问,该句柄由THandleStream的构造方法Create传入。因此除了用THandleStream提供的方法外,也可以用文件管理函数对句柄进行操作。实际上,THandleStream的方法在实现上也是运用文件管理函数进行实际的读写操作。

  2. Create方法

  声明:constructor Create(AHandle: Integer);

  Create方法使用传入的Handle参数创建一个与特定文件句柄相联的THandleStream对象,并且将AHandle赋给流的Handle属性。

 

  3. ReadWriteSeek方法

  这三个方法是TStream的虚方法,只是在THandleStream 中覆盖了这三个方法,以实现特定媒介──文件的数据存取。后面会详细介绍这三个方法的实现。

 

20.1.2.2 THandleStream的实现原理

 

  THandleStream是从TStream继承来的,因此可以共用TStream中的属性和大多数方法。THandleStream在实现上主要是增加了一个属性Handle和覆盖了CreateReadWriteSeek四个方法。

  1. 属性的实现

  Handle属性的实现正如Delphi大多数属性的实现那样,先在对象定义的private部分声明一个存放数据的变量FHandle,然后在定义的public部分声明属性Handle,其中属性定义的读写控制部分加上只读控制,读控制只是直接读取FHandle变量的值,其实现如下:

 

THandleStream = class(TStream)

private

FHandle: Integer;

public

property Handle: Integer read FHandle;

end;

 

2. 方法的实现

  THandleStreamCreate方法,以AHandle作为参数,在方法里面只是简单的将AHandle的值赋给FHandle,其实现如下:

 

constructor THandleStream.Create(AHandle: Integer);

begin

FHandle := AHandle;

end;

 

  为实现针对文件的数据对象存储,THandleStreamReadWriteSeek方法覆盖了TStream中的相应方法。它们的实现都调用了Windows的文件管理函数。

  Read方法调用FileRead函数实现文件读操作,其实现如下:

 

function THandleStream.Read(var Buffer; Count: Longint): Longint;

begin

Result := FileRead(FHandle, Buffer, Count);

if Result = -1 then Result := 0;

end;

 

  Write方法调用FileWrite函数实现文件写操作,其实现如下:

 

function THandleStream.Write(const Buffer; Count: Longint): Longint;

begin

Result := FileWrite(FHandle, Buffer, Count);

if Result = -1 then Result := 0;

end;

 

  Seek方法调用FileSeek函数实现文件指针的移动,其实现如下:

 

function THandleStream.Seek(Offset: Longint; Origin: Word): Longint;

begin

Result := FileSeek(FHandle, Offset, Origin);

end;

 

20.1.3 TFileStream对象

 

  TFileStream对象是在磁盘文件上存储数据的Stream对象。TFileStream是从THandleStream继承下来的,它和THandleStream一样都是实现文件的存取操作。不同之处在于THandleStream用句柄访问文件,而TFileStream用文件名访问文件。实际上TFileStreamTHandleStream上的一层包装,其内核是THandleStream的属性和方法。

  TFileStream中没有增加新的属性和方法。它只是覆盖了的构造方法Create和析构方法Destory。在Create方法中带两个参数FileNameModeFileName描述要创建或打开的文件名,而Mode描述文件模式如fmCreatefmOpenReadfmOpenWrite等。Create方法首先使用FileCreateFileOpen函数创建或打开名为FileName的文件,再将得到的文件句柄赋给FHandleTFileStream的文件读写操作都是由从THandleStream继承的Read<FONT< P>

var

Stream: TStream;

begin

Stream := TFileStream.Create(FileName, fmCreate);

try

SaveToStream(Stream);

finally

Stream.Free;

end;

end;

 

  在Delphi 的许多对象的SaveToStream SaveToFileLoadFromStreamLoadFromFile方法的实现都有类似的嵌套结构。

 

20.1.5 TMemoryStream对象

 

  TMemoryStream对象是一个管理动态内存中的数据的Stream对象,它是从TCustomMemoryStream中继承下来的,除了从TCustomMemoryStream中继承的属性和方法外,它还增加和覆盖了一些用于从磁盘文件和其它注台读数据的方法。它还提供了写入、消除内存内容的动态内存管理方法。下面介绍它的这些属性和方法。

 

20.1.5.1 TMemoryStream的属性和方法

 

  1. Capacity属性

  声明:property Copacity: Longint;

Capacity属性决定了分配给内存流的内存池的大小。这与Size属性有些不同。Size属性是描述流中数据的大小。在程序中可以将Capacity 的值设置的比数据所需最大内存大一些,这样可以避免频繁地重新分配。

  2. Realloc方法

  声明:function Realloc(var NewCapacity: Longint): Pointer; virtual;

Realloc方法,以8K为单位分配动态内存,内存的大小由NewCapacity指定,函数返回指向所分配内存的指针。

  3. SetSize方法

  SetSize方法消除内存流中包含的数据,并将内存流中内存池的大小设为Size字节。如果Size为零,是SetSize方法将释放已有的内存池,并将Memory属性置为nil;否则,SetSize方法将内存池大小调整为Size

4. Clear方法

  声明:procedure Clear;

Clear方法释放内存中的内存池,并将Memory属性置为nil。在调用Clear方法后,SizePosition属性都为0

  5. LoadFromStream方法

  声明:procedure LoadFromStream(Stream: TStream);

LoadFromStream方法将Stream指定的流中的全部内容复制到MemoryStream中,复制过程将取代已有内容,使MemoryStream成为Stream的一份拷贝。

  6. LoadFromFile方法

  声明:procedure LoadFromFile(count FileName: String);

LoadFromFile方法将FileName指定文件的所有内容复制到MemoryStream中,并取代已有内容。调用LoadFromFile方法后,MemoryStream将成为文件内容在内存中的完整拷贝。

 

 

20.1.5.2 TMemoryStream对象的实现原理

 

  TMemoryStreamTCustomMemoryStream对象直接继承,因此可以享用TCustomMemoryStream的属性和方法。前面讲过,TCustomMemoryStream是用于内存中数据操作的抽象对象,它为MemoryStream对象的实现提供了框架,框架中的内容还要由具体MemoryStream对象去填充。TMemoryStream对象就是按动态内存管理的需要填充框架中的具体内容。下面介绍TMemoryStream对象的实现。

  1. TMemoryStream属性的实现

  TMemoryStream在其protected部分增加了一个Capacity属性,该属性决定了MemoryStream所占动态内存的大小。TMemoryStream首先在private部分声明了FCapacity变量作为存储Capacity属性值的数据域,然后在protected部分声明了该属性。在属性声明的读控制部分简单读取FCapacity的值,在写控制处调用了方法SetCapacity。该方法除了给FCapacity赋值外还执行了修改Capacity属性所必需操作如状态改变等。

  下面是属性的实现:

 

TMemoryStream = class(TCustomMemoryStream)

private

FCapacity: Longint;

procedure SetCapacity(NewCapacity: Longint);

protected

property Capacity: Longint read FCapacity write SetCapacity;

public

end;

 

  写控制方法SetCapacity的实现是这样的:

 

procedure TMemoryStream.SetCapacity(NewCapacity: Longint);

begin

SetPointer(Realloc(NewCapacity), FSize);

FCapacity := NewCapacity;

end;

 

  在SetCapacity 方法先是调用Realloc重新分配内存,然后用NewCapacity的值给FCapacity赋值。Realloc方法进行某些对象状态的改变。

  2. TMemoryStream对象方法的实现

  ⑴ Realloc方法

  Realloc方法是TMemoryStream动态内存分配的核心,它的SetSizeSetCapacity等方法最终都是调用Realloc进行内存的分配和初始化工作的。它的实现如下:

 

const

MemoryDelta = $2000;

 

function TMemoryStream.Realloc(var NewCapacity: Longint): Pointer;

begin

if NewCapacity > 0 then

NewCapacity := (NewCapacity + (MemoryDelta - 1)) and not (MemoryDelta - 1);

Result := Memory;

if NewCapacity <> FCapacity then

begin

if NewCapacity = 0 then

begin

GlobalFreePtr(Memory);

Result := nil;

end else

begin

if Capacity = 0 then

Result := GlobalAllocPtr(HeapAllocFlags, NewCapacity)

else

Result := GlobalReallocPtr(Memory, NewCapacity, HeapAllocFlags);

if Result = nil then raise EStreamError.CreateRes(SMemoryStreamError);

end;

end;

end;

 

  Realloc方法是以8K为单位分配动态内存的,方法中的第一句if语句就是执行该操作。如果传入的NewCapacity参数值为0,则释放流中的内存。Realloc方法用GLobal FreePtr函数释放内存,用GlobalAllocPtr分配内存,用GlobalReallocPtr进行内存的重分配。如果原来的Capacity属性值为0,则调用Globa|AllocPtr否则调用GlobalReallocPtr。最后如果Resultnil则触发内存流错的异常事件,否则返回指向分配的内存的指针。

  ⑵ Write方法

  Write方法从内存流内部缓冲池的当前位置开始写入二进制数据。其实现如下:

 

function TMemoryStream.Write(const Buffer; Count: Longint): Longint;

var

Pos: Longint;

begin

if (FPosition >= 0) and (Count >= 0) then

begin

Pos := FPosition + Count;

if Pos > 0 then

begin

if Pos > FSize then

begin

if Pos > FCapacity then

SetCapacity(Pos);

FSize := Pos;

end;

System.Move(Buffer, Pointer(Longint(FMemory) + FPosition)^, Count);

FPosition := Pos;

Result := Count;

Exit;

end;

end;

Result := 0;

end;

 

  Buffer中存储要写入流的二进制数据,如果要写入的数据的字节超出了流的内存池的大小,则调用SetCapacity方法再分配内存,然后用内存复制函数将Buffer中的数据复制到FMemory中。接着移动位置指针,并返回写入数据的字节数。分析这段程序可以知道,FCapacity的值和FSize的值是不同的。

  ⑶ Clear方法

  Clear方法消除内存流中的数据,将Memory属性置为nil,并将FSizeFPosition 的值设为0。其实现如下:

 

procedure TMemoryStream.Clear;

begin

SetCapacity(0);

FSize := 0;

FPosition := 0;

end;

 

  ⑷ LoadFromStreamLoadFromFile方法

  LoadFromStream方法首先根据传入的StreamSize属性值重新分配动态内存,然后调用StreamReadBuffer方法往FMemory中复制数据,结果Stream的全部内容在内存中有了一份完整拷贝。其实现如下:

 

procedure TMemoryStream.LoadFromStream(Stream: TStream);

var

Count: Longint;

begin

Stream.Position := 0;

Count := Stream.Size;

SetSize(Count);

if Count <> 0 then Stream.ReadBuffer(FMemory^, Count);

end; 

  LoadFromFileLoadFromStream是一对方法。LoadFromFile首先创建了一个TFileStream对象,然后调用LoadFromStream方法,将FileStream文件流中的数据写入MemoryStream中。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows中的通用应用程序的类名 使用API函数复制移动文件 使用API访问ListBox项 使用GetTempFileName创建一个唯一的临时文件 使用INI文件 使用INI文件保存、装载字体信息 使用TFileStream 使用TStream保存字符串 使用TTreeview显示目录 使窗体的关闭按钮失效 修改文本文件 允许在资源管理器中拖放文件 减小EXE文件大小 列举驱动器 列出目录下的子目录 创建快捷方 创建目录 删除文件到回收站 判断文件夹是否共享 剪贴板存放多个控件流 加载CDROM图标 压缩和解压流 取MP3的ID3-Tag 取Program files目录 取可执行文件类型 取和文件类型关联的应用程序 取当前程序所在目录 取指定文件的版本信息 取文件修改日期 取文件日期 取文件最后访问日期 取文件版本号 取文件的所有者 取目录大小 取磁盘可用空间和总空间 取设置当前目录 取设置文件夹的日期 向Exe文件中添加数据 向文件写添加文本 在TMemo光标位置插入一个文件 在Windows开始后自动运行一个程序 在应用程序中添加Exe文件并且执行 在文件中搜索字符串 在文件中搜索指定文本 在文本文件中搜索文本 将剪贴板复制到流和恢复 将文本文件赋值给一个字符串 将资源文件中Rft文本装载到TRichEdit 将长文件名转换成短文件名 彻底删除文件 打开资源管理器且显示指定文件夹 捕获DOS应用程序的输出 改变TPageControls的颜色 改名、移动、删除文件或目录 显示‘打开方’对话框 显示文件属性对话框 显示目录选择对话框 显示目录选择对话框并指定初始目录 替换正运行的DLL 检查文件是否ASCII格 检查文件是否在本地驱动器 检查文件是否已打开 检查文件是否正在使用 检查目录是否存在 比较两个文件是否相同 添加去掉路径名后的符号 添加文件到文档菜单 清空文档 获取文件类型 获得快捷方信息 获得文件大小 计算文件的校验和 设置文件日期 读 table-textfile 到 StringGrid 读取二进制文件使用ASCII显示 转换OEM到ANSI 转换短文件名为长文件名 运行一个程序或打开一个关联文件 返回UNC路径 通过CRC-32验证文件
好的,以下是一个简单的 Delphi 多线程程序,可以实时接收数据流并保存到文件: ```delphi unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TMyThread = class(TThread) private FFileName: string; FData: TStream; protected procedure Execute; override; public constructor Create(FileName: string; Data: TStream); end; TForm1 = class(TForm) Memo1: TMemo; BtnStart: TButton; procedure BtnStartClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private FThreadList: TThreadList; end; var Form1: TForm1; implementation {$R *.dfm} constructor TMyThread.Create(FileName: string; Data: TStream); begin inherited Create(True); FFileName := FileName; FData := Data; end; procedure TMyThread.Execute; var FileStream: TFileStream; Buffer: array[0..1023] of Byte; Count: Integer; begin try FileStream := TFileStream.Create(FFileName, fmCreate); try while not Terminated do begin Count := FData.Read(Buffer, SizeOf(Buffer)); if Count > 0 then FileStream.Write(Buffer, Count) else Sleep(10); // wait for more data end; finally FileStream.Free; end; except // do nothing end; end; procedure TForm1.BtnStartClick(Sender: TObject); var FileName: string; Data: TMemoryStream; Thread: TMyThread; begin FileName := FormatDateTime('yyyymmddhhnnss', Now) + '.bin'; Data := TMemoryStream.Create; try // read data from somewhere // ... // save data to file using a thread Thread := TMyThread.Create(FileName, Data); FThreadList.Add(Thread); Thread.Start; finally Data.Free; end; end; procedure TForm1.FormCreate(Sender: TObject); begin FThreadList := TThreadList.Create; end; procedure TForm1.FormDestroy(Sender: TObject); var I: Integer; Thread: TThread; begin FThreadList.LockList; try for I := 0 to FThreadList.Count - 1 do begin Thread := TThread(FThreadList[I]); Thread.Terminate; Thread.WaitFor; Thread.Free; end; finally FThreadList.UnlockList; FThreadList.Free; end; end; end. ``` 这个程序中,我们创建了一个 `TMyThread` 类,用于在后台线程中实时接收数据流并保存到文件。在主线程中,我们通过点击按钮触发接收操作,并将数据传递给后台线程。程序中使用了 `TThreadList` 来管理所有的后台线程,以便在程序退出时能够等待所有线程结束。注意,这个程序只是一个简单的示例,实际应用中需要根据具体需求进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值