Delphi 中流的使用(1) 用 TMemoryStream(内存流) 入门
前言:
所谓"流", 就是一段数据或是一块内存;
在进行流操作时, 我们不必关心流中的数据到底是什么; 只需要知道流的大小和当前的指针位置. 所以流只有两个属性:
Size、Position.
对流的操作, 不过就是读取和写入. 所以流最主要的方法就是 Read 和 Write.
在很多控件的使用中, 读取主要用 LoadFromStream; 写入主要用 SaveToStream.
举个例子: (建立新工程, 添加两个 Memo、两个 Button)
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Memo2: TMemo; Button1: TButton; Button2: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormDestroy(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} var mStream: TStream; {声明一个流对象} procedure TForm1.FormCreate(Sender: TObject); begin mStream := TMemoryStream.Create; {TStream 是抽象类, 只能通过其子类实例化; 这里我们用了内存流来生成实例} Memo1.Lines.Text := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; {给 Memo1 个初始值} end; procedure TForm1.Button1Click(Sender: TObject); begin Memo1.Lines.SaveToStream(mStream); {把 Memo1 中的内容写入到流} ShowMessage(IntToStr(mStream.Size)); {26, 当前流的大小} ShowMessage(IntToStr(mStream.Position)); {26, 当前流的指针} end; procedure TForm1.Button2Click(Sender: TObject); begin mStream.Position := 4; {调整流的当前指针位置} Memo2.Lines.LoadFromStream(mStream); {读出流中的内容到 Memo2} { 现在 Memo2 中的内容应该是: EFGHIJKLMNOPQRSTUVWXYZ 如果 Position 是 0, Memo2 读出的内容会是: ABCDEFGHIJKLMNOPQRSTUVWXYZ 如果 Position 等于 Size, 在这里如果是 26, Memo2 就读不出什么了. } end; procedure TForm1.FormDestroy(Sender: TObject); begin mStream.Free; {流释放时, 所用内存当然也会同时释放} end; end.
用TFileStream(文件流进行读写)
TStream 是一个抽象的基类, 不能直接生成对象. 在具体的应用中, 主要使用它的子孙类:
TFileStream: 文件流
TStringStream: 字符串流
TMemoryStream: 内存流
TResourceStream: 资源文件流
THandleStream: 是 TFileStream 的父类、TStream 的子类
TCustomMemoryStream: 是 TMemoryStream 和 TResourceStream 的父类、TStream 的子类
与流相关的常用类还有: TReader、TWriter、TCompressionStream、TDecompressionStream
来一个文件流的例子:
procedure TForm1.Button1Click(Sender: TObject); var getStream,setStream: TFileStream; {声明一个文件流} getPath,setPath: string; begin getPath := 'c:/temp/get.jpg'; {需要这个文件存在} setPath := 'c:/temp/set.jpg'; {这个会自动建立} if not FileExists(getPath) then begin ShowMessage('找不到我们要测试的图片文件: ' + getPath); Exit; end; getStream := TFileStream.Create(getPath, fmOpenRead or fmShareExclusive); setStream := TFileStream.Create(setPath, fmCreate); {建立文件流需要两个参数: 参数 1 是路径, 参数 2 是打开模式} getStream.Position := 0; {流指针移到开始, 复制时从这里开始} setStream.CopyFrom(getStream, getStream.Size); {Copy 流} {CopyFrom 的参数 2 是要复制的内容大小; 如果为 0 , 不管指针在什么位置都会复制所有内容} {CopyFrom 返回实际拷贝的字节数} {这时硬盘上就有 set.jpg 文件, 与 get.jpg 一么一样} {其实就是复制文件, 不过这里我们是用文件流实现的} getStream.Free; setStream.Free; end;TFileStream 打开模式与共享模式:
分类 | 参数 | 说明 |
---|---|---|
打 开 模 式 |
fmCreate | 建立文件, 如果存在则打开它。 |
fmOpenRead | 只读打开 | |
fmOpenWrite | 只写打开 | |
fmOpenReadWrite | 读写打开 | |
共 享 模 式 |
fmShareCompat | 共享模式, 兼容 Dos |
fmShareExclusive | 不允许别人以任何方式打开 | |
fmShareDenyWrite | 允许别人以只写方式打开 | |
fmShareDenyRead | 允许别人以只读方式打开 | |
fmShareDenyNone | 允许别人以任何方式打开 |
通过内存流读取文件
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; Button2: TButton; Button3: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} var mStream: TMemoryStream; procedure TForm1.FormCreate(Sender: TObject); //程序开始先创建一个准备要测试的文件 var strList: TStringList; begin strList := TStringList.Create; strList.Add('aaaaaaaa'); strList.Add('bbbbbbbb'); strList.Add('cccccccc'); strList.Add('dddddddd'); strList.SaveToFile('c:/temp/test.txt'); strList.Free; {同时建立内存流} mStream := TMemoryStream.Create; end; procedure TForm1.Button1Click(Sender: TObject); //通过流读文件到 Memo begin mStream.LoadFromFile('c:/temp/test.txt'); {把文件读入内存流} Memo1.Lines.LoadFromStream(mStream); {把内存流载入 Memo1} end; procedure TForm1.Button2Click(Sender: TObject); //用字符指针读取流中的内容 var pc: PChar; begin pc := mStream.Memory; {把字符指针指向内存流} ShowMessage(pc[0]); {a; 第一个字符} ShowMessage(pc[10]); {b; 这个第二行的第一个字符; 每行8个字再加换行与回车共10个字符} ShowMessage(pc[20]); {c} ShowMessage(pc[30]); {d} end; procedure TForm1.Button3Click(Sender: TObject); //从流读入到缓冲区 var buffer: array[0..2] of Char; {定义个字符缓冲区} begin mStream.Seek(0, soFromBeginning); mStream.Read(buffer, SizeOf(buffer)); ShowMessage(buffer); {aaa} mStream.Seek(10, soFromBeginning); mStream.Read(buffer, SizeOf(buffer)); ShowMessage(buffer); {bbb} mStream.Seek(20, soFromBeginning); mStream.Read(buffer, SizeOf(buffer)); ShowMessage(buffer); {ccc} mStream.Seek(30, soFromBeginning); mStream.Read(buffer, SizeOf(buffer)); ShowMessage(buffer); {ddd} {关于 Seek 函数: 参数1: Offset 是偏移量; 参数2: Origin 是指针的基准位置, 有三个选值: soFromBeginning、soFromCurrent、soFromEnd soFromBeginning: 以开始为基准, 此时参数 Offset 要 >= 0; soFromCurrent: 以当前位置为基准; soFromEnd: 以结束为基准; 此时参数 Offset 要 <= 0; 返回: 指针新位置 } end; procedure TForm1.FormDestroy(Sender: TObject); begin mStream.Free; end; end.
遍历读取流中的所有数据
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Memo2: TMemo; {需要添加两个 Memo 用于显示} Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} //先制造一个测试文件 procedure TForm1.FormCreate(Sender: TObject); var strList: TStringList; begin strList := TStringList.Create; strList.Add('ABCDEFGHIJKLMNOPQRSTUVWXYZ'); strList.SaveToFile('c:/temp/test.txt'); strList.Free; end; procedure TForm1.Button1Click(Sender: TObject); var ms: TMemoryStream; c: Char; s1,s2: string; begin ms := TMemoryStream.Create; ms.LoadFromFile('c:/temp/test.txt'); {读入内存流} s1 := ''; s2 := ''; ms.Position := 0; {指针到开始} while ms.Position < ms.Size do {循环读出} begin ms.Read(c,1); {每读出一个字节, 指针会自动移到新的位置} s1 := s1 + c + ' '; {用文本记录} s2 := s2 + IntToHex(Byte(c),2) + ' '; {用两位数的十六进制记录} end; Memo1.Lines.Text := s1; Memo2.Lines.Text := s2; {Memo1 会显示: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z } {Memo2 会显示: 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 0D 0A} ms.Free; end; end.
组件序列化
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; Button4: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} //添加一个 Memo1 然后修改其内容 procedure TForm1.Button1Click(Sender: TObject); begin WriteComponentResFile('c:/temp/memo.dat', Memo1); {只此一句就可以把当前的 Memo 的状态序列化到文件} end; //反序列化, 读回 procedure TForm1.Button2Click(Sender: TObject); begin ReadComponentResFile('c:/temp/memo.dat', Memo1); {一句话就可以读回, 不管是经过了什么操作(甚至是关机)} end; { 这好像和流没什么关系, 其实这就是流的典型操作, WriteComponentResFile 和 ReadComponentResFile 分别调用了流类的 WriteComponentRes 与 ReadComponentRes 方法. 下面用更直接的流的方式重新实现一次: } //序列化 procedure TForm1.Button3Click(Sender: TObject); var stream: TStream; const strPath = 'c:/temp/m.dat'; begin stream := TFileStream.Create(strPath, fmCreate); stream.WriteComponentRes(Memo1.ClassName, Memo1); {WriteComponentRes 有两个参数: 文件名和组件名} stream.Free; end; //反序列化 procedure TForm1.Button4Click(Sender: TObject); var stream: TStream; const strPath = 'c:/temp/m.dat'; begin stream := TFileStream.Create(strPath, fmOpenRead); stream.ReadComponentRes(Memo1); {ReadComponentRes 只有一个参数: 组件名} stream.Free; end; end.
用流读写结构化文件
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; {添加 Memo 显示内容} Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} type TRec = record {定义一个记录} name: string[8]; age: Word; end; //写入 procedure TForm1.Button1Click(Sender: TObject); var rec: TRec; ms: TMemoryStream; begin ms := TMemoryStream.Create; rec.name := '张三'; rec.age := 8; ms.Write(rec, SizeOf(rec)); rec.name := '李四'; rec.age := 81; ms.Write(rec, SizeOf(rec)); rec.name := '王二麻子'; rec.age := 18; ms.Write(rec, SizeOf(rec)); ms.SaveToFile('c:/temp/rec.dat'); ms.Free; end; //读取 procedure TForm1.Button2Click(Sender: TObject); var rec: TRec; ms: TMemoryStream; begin ms := TMemoryStream.Create; ms.LoadFromFile('c:/temp/rec.dat'); Memo1.Clear; ms.Position := 0; while ms.Position < ms.Size do begin ms.Read(rec, SizeOf(rec)); Memo1.Lines.Add(rec.name + ' ' + IntToStr(rec.age)); end; {Memo1 的显示结果会是: 张三 8 李四 81 王二麻子 18 } ms.Free; end; end.
压缩与解压
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} uses Zlib; {压缩流 TCompressionStream 与解压缩流 TDecompressionStream 来自 Zlib 单元} //压缩 procedure TForm1.Button1Click(Sender: TObject); var cs: TCompressionStream; {定义压缩流} fs,ms: TMemoryStream; {fs 是要压缩的流; ms 是接收压缩后文件的流} num: Integer; {原始文件大小} begin {第一步: 调入要压缩的文件, 并获取大小} fs := TMemoryStream.Create; fs.LoadFromFile('c:/temp/test.txt'); {文件要存在啊} num := fs.Size; {第二步: 建立接收的流, 并先写入原始文件大小} ms := TMemoryStream.Create; ms.Write(num, SizeOf(num)); {第三步: 压缩} cs := TCompressionStream.Create(clMax, ms); {参数1是压缩比; 参数2是接收流} fs.SaveToStream(cs); {传入要压缩的数据} cs.Free; {压缩流 Free 后才真正完成压缩, 所以提前 Free} {第四步: 保存} ms.SaveToFile('c:/temp/test.zipx'); {第五步: 释放} ms.Free; fs.Free; {压缩比参数: clNone 无压缩 clFastest 快速 clDefault 默认 clMax 最大比例 } end; //解压缩 procedure TForm1.Button2Click(Sender: TObject); var ds: TDecompressionStream; {解压流} fs,ms: TMemoryStream; {fs 是准备要解压的流; ms 是接受解压数据的流} num: Integer; {接受文件压缩前的大小} begin {第一步: 准要解压的文件} fs := TMemoryStream.Create; fs.LoadFromFile('c:/temp/test.zipx'); {必须是上一个压缩方法生成的文件} {第二步: 读出文件压缩前的大小} fs.Position := 0; fs.ReadBuffer(num,SizeOf(num)); {第三步: 准备好要接收的流, 并设置需要的大小} ms := TMemoryStream.Create; ms.SetSize(num); {第四步: 解压} ds := TDecompressionStream.Create(fs); {参数是要解压的流} {第五步: 把解压后的数据读出存放到已准备好接收的流} ds.Read(ms.Memory^, num); {第六步: 保存} ms.SaveToFile('c:/temp/test2.txt'); ds.Free; ms.Free; fs.Free; end; end.
压缩与解压缩的函数
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} uses Zlib; //压缩函数 procedure Zip(var fs: TMemoryStream); var cs: TCompressionStream; ms: TMemoryStream; num: Integer; begin if not(Assigned(fs) and (fs.Size>0)) then Exit; num := fs.Size; ms := TMemoryStream.Create; cs := TCompressionStream.Create(clMax, ms); try fs.SaveToStream(cs); cs.Free; //ms.Position := 0; fs.Clear; fs.WriteBuffer(num, sizeof(num)); fs.CopyFrom(ms, 0); finally ms.Free; end; end; //解压函数 procedure UnZip(var fs: Tmemorystream); var ds: TDecompressionStream; ms: TMemoryStream; num: Integer; begin if not(Assigned(fs) and (fs.Size>0)) then Exit; fs.Position := 0; fs.ReadBuffer(num,sizeof(num)); ms := TMemoryStream.Create; ds := TDecompressionStream.Create(fs); try ms.SetSize(num); ds.Read(ms.Memory^, num); //ms.Position := 0; fs.Clear; fs.CopyFrom(ms, 0); finally ds.Free; ms.Free; end; end; //压缩测试 procedure TForm1.Button1Click(Sender: TObject); var ms: TMemoryStream; begin ms := TMemoryStream.Create; ms.LoadFromFile('c:/temp/test.txt'); Zip(ms); ms.SaveToFile('c:/temp/test.zipx'); end; //解压测试 procedure TForm1.Button2Click(Sender: TObject); var ms: TMemoryStream; begin ms := TMemoryStream.Create; ms.LoadFromFile('c:/temp/test.zipx'); UnZip(ms); ms.SaveToFile('c:/temp/test2.txt'); end; end.
分割与合并文件的函数
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} //分割文件的函数 {参数 1 是要分割的文件名; 参数 2 是要风格文件的大小, 单位是 KB} {分割后的文件名扩展名用序号替换} function SplitFile(const FileName: string; Size: Cardinal): Boolean; var fStream: TFileStream; {原始文件} toStream: TMemoryStream; {分文件} p,i: Integer; {p 记录当前指针位置; i 记录这是第几个分的文件} begin Result := False; Size := Size * 1024; {把大小的单位转换为字节} fStream := TFileStream.Create(FileName, fmOpenRead); p := 0; i := 0; toStream := TMemoryStream.Create; while p < fStream.Size do begin toStream.Clear; {清空上次数据} fStream.Position := p; {放好指针位置} if fStream.Size-p < Size then Size := fStream.Size-p; {最后一个时, 有多少算多少} toStream.CopyFrom(fStream, Size); {复制} toStream.SaveToFile(FileName + '.' + IntToStr(i)); {保存} Inc(i); p := p + Size; end; fStream.Free; toStream.Free; Result := True; end; //合并文件, 参数是其中一个分文件名 function MergeFile(const FileName: string): Boolean; var ms: TMemoryStream; {读取分文件} fs: TFileStream; {合并后的文件} path: string; i: Integer; begin path := ChangeFileExt(FileName,''); {去掉序号扩展名} ShowMessage(path); i := 0; ms := TMemoryStream.Create; fs := TFileStream.Create(path, fmCreate); while FileExists(path + '.' + IntToStr(i)) do begin ms.LoadFromFile(path + '.' + IntToStr(i)); fs.CopyFrom(ms, 0); {TFileStream 不需要 SetSize; 但如果用 TMemoryStream 就需要} Inc(i); end; ms.Free; fs.Free; end; //测试分割 procedure TForm1.Button1Click(Sender: TObject); begin SplitFile('c:/temp/test.txt', 10); end; //测试合并 procedure TForm1.Button2Click(Sender: TObject); begin MergeFile('c:/temp/test.txt.0'); end; end.
压缩与解压缩进度
本例是在这个例子的基础上修改的: http://www.cnblogs.com/del/archive/2008/01/01/1022539.html
本例效果图:
代码文件:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; ProgressBar1: TProgressBar; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure CsProgress(Sender: TObject); {压缩的 OnProgress 事件} procedure DsProgress(Sender: TObject); {解压缩的 OnProgress 事件} end; var Form1: TForm1; implementation {$R *.dfm} uses Zlib; {压缩的 OnProgress 事件} procedure TForm1.CsProgress(Sender: TObject); begin ProgressBar1.Position := Integer(TCompressionStream(Sender).Position div 1024); Application.ProcessMessages; end; {解压缩的 OnProgress 事件} procedure TForm1.DsProgress(Sender: TObject); begin ProgressBar1.Position := Integer(TDecompressionStream(Sender).Position div 1024); Application.ProcessMessages; end; {压缩} procedure TForm1.Button1Click(Sender: TObject); var cs: TCompressionStream; fs,ms: TMemoryStream; num: Integer; begin fs := TMemoryStream.Create; fs.LoadFromFile('c:/temp/test.txt'); {我是用一个 15M 的文本文件测试的} num := fs.Size; ms := TMemoryStream.Create; ms.Write(num, SizeOf(num)); cs := TCompressionStream.Create(clMax, ms); {在原来代码基础是添加这两行} ProgressBar1.Max := Integer(fs.Size div 1024); cs.OnProgress := CsProgress; fs.SaveToStream(cs); cs.Free; ms.SaveToFile('c:/temp/test.zipx'); ms.Free; fs.Free; end; {解压缩} procedure TForm1.Button2Click(Sender: TObject); var ds: TDecompressionStream; fs,ms: TMemoryStream; num: Integer; begin fs := TMemoryStream.Create; fs.LoadFromFile('c:/temp/test.zipx'); fs.Position := 0; fs.ReadBuffer(num,SizeOf(num)); ms := TMemoryStream.Create; ms.SetSize(num); ds := TDecompressionStream.Create(fs); {在原来代码基础是添加这两行} ProgressBar1.Max := Integer(ms.Size div 1024); ds.OnProgress := DsProgress; ds.Read(ms.Memory^, num); ms.SaveToFile('c:/temp/test2.txt'); ds.Free; ms.Free; fs.Free; end; end.窗体文件:
object Form1: TForm1 Left = 0 Top = 0 Caption = 'Form1' ClientHeight = 136 ClientWidth = 205 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 object Button1: TButton Left = 64 Top = 24 Width = 75 Height = 25 Caption = #21387#32553 TabOrder = 0 OnClick = Button1Click end object Button2: TButton Left = 64 Top = 55 Width = 75 Height = 25 Caption = #35299#21387#32553 TabOrder = 1 OnClick = Button2Click end object ProgressBar1: TProgressBar Left = 24 Top = 97 Width = 150 Height = 17 TabOrder = 2 end end