ZLib的数据压缩和解压缩
Delphi的开发者可以使用ZLib单元中定义的TCompressionStream进行数据压缩,使用TDecompressionStream解压缩ZLib压缩后的数据。它们的定义如下:
private
FStrm : TStream;
FStrmPos : Integer ;
FOnProgress : TNotifyEvent;
FZRec : TZStreamRec;
FBuffer : array [Word] of Char;
protected
procedure Progress(Sender : TObject); dynamic;
property OnProgress : TNotifyEvent read FOnProgress write FOnProgress;
constructor Create(Strm : TStream);
end ;
TCompressionStream = class (TCustomZlibStream)
private
function GetCompressionRate : Single;
public
constructor Create(CompressionLevel : TCompressionLevel; Dest : TStream);
destructor Destroy; override;
function Read( var Buffer; Count : Longint) : Longint; override;
function Write( const Buffer; Count : Longint) : Longint; override;
function Seek(Offset : Longint; Origin : Word) : Longint; override;
property CompressionRate : Single read GetCompressionRate;
property OnProgress;
end ;
TDecompressionStream = class (TCustomZlibStream)
public
constructor Create(Source : TStream);
destructor Destroy; override;
function Read( var Buffer; Count : Longint) : Longint; override;
function Write( const Buffer; Count : Longint) : Longint; override;
function Seek(Offset : Longint; Origin : Word) : Longint; override;
property OnProgress;
end ;
要使用这两个类,必须通过流(Stream)。事实上,它们的基类TCustomZlibStream就是TStream的派生类。他们的构造函数需要传入一个TStream的派生类对象,但是它们并不拥有(说准确点,是管理)这个传入流对象,代码编写者负责管理其生存期,传入构造函数参数后,它们会记住传入流的当前Position,之后的读或写将从这个位移开始。虽然 TCompressionStream 和 TDecompressionStream 都是流类,但是它们还是有特殊性的: TCompressionStream是只写的。调用其Read方法将引发一个异常;而 TDecompressionStream 是只读的,调用其Write方法也将引发异常。TCompressionStream把外界写入的数据压缩后存进其内部缓冲区,填满时把缓冲数据写进构造函数中传入的流对象,然后清空缓冲区,继续接受数据,如此循环往复。TCompressionStream的析构函数中会检查缓冲区中是否有数据,有的话不管是否填满,都写进构造函数中传入的流对象,因此一般来说,在TCompressionStream释放后,传入的流对象才包含了完整的压缩后数据。TDecompressionStream把其构造函数中传入的流对象包好的数据压缩后压缩后存储在缓冲区中,所以正确地构造TDecompressionStream对象后,就可以使用TDecompressionStream的Read方法来读取解压后的数据了。下面是示例代码:
{压缩,将ASrcStream包含的数据压缩,返回的TStream对象中包含了压缩结果数据}
var
ACompStrm : TCompressionStream;
OriginSize : Int64;
begin
Result := TMemoryStream . Create;
OriginSize := ASrcStream . Size;
// 写入原始数据的字节数,方便解压时预先获知数据字节数
Result . Write(OriginSize , SizeOf (OriginSize));
ACompStrm := TCompressionStream . Create(TCompressionLevel(ALevel) , Result);
try
try
// CopyFrom内部还是调用Write写入待压缩数据,只要写入,都将被压缩
ACompStrm . CopyFrom(ASrcStream , 0 );
except
on E : Exception do SfRaiseException(E);
end ;
finally
SfFreeAndNil(ACompStrm);
end ;
end ;
{解压缩数据,ASrcStream包含ZLib压缩数据,函数返回的TStream对象包含解压结果数据}
function DecompressStream(ASrcStream : TStream) : TStream;
var
ADecompStrm : TDecompressionStream;
OriginSize : Int64;
begin
ASrcStream . Position := 0 ;
// 读取原始数据大小,在解压前就可以预知解压结果的字节数
ASrcStream . Read(OriginSize , SizeOf (OriginSize));
ADecompStrm := TDecompressionStream . Create(ASrcStream);
Result := TMemoryStream . Create;
try
try
// 读取解压结果
Result . CopyFrom(ADecompStrm , OriginSize);
except
on E : Exception do SfRaiseException(E);
end ;
finally
SfFreeAndNil(ADecompStrm);
end ;
end ;
示例代码中的SfRaiseException和SfFreeAndNil是偶项目中的自定义的函数,跟理解示例代码没有关联,所以不浪费时间说明它们。TSfCompressionLevel(压缩方式)只是对ZLib.pas中TCompressionLevel枚举的直接映射。
用TCompressionStream和TDecompressionStream实在麻烦,有没有直接的函数呢?有!而且就在ZLib.pas中,它们们是:
procedure DecompressBuf( const InBuf : Pointer; InBytes : Integer ;OutEstimate : Integer ; out OutBuf : Pointer; out OutBytes : Integer );
procedure DecompressToUserBuf( const InBuf : Pointer; InBytes : Integer ; const OutBuf : Pointer; BufSize : Integer );
CompressBuf和DecompressBuf的参数类似,InBuf待压缩/解压的数据,InBytes为字节数。OutBuf为压缩/解压结果,OutBytes为结果字节数。对于DecompressBuf的OutEstimate,我们传入0就可以了。这两个函数内部自行分配存储结果数据所需要的内存,这是它们与DecompressToUserBuf的不同之处。DecompressToUserBuf要求调用者调用前就分配足够的内存来存储解压结果,它适用于预先知道原始数据字节数的情况,如果分配的内存不足,会引发异常。CompressBuf和DecompressBuf内部会处理异常,发生异常时释放为OutBuf分配的内存,然后重引发异常,函数的调用者要注意不要重复释放OutBuf,只有调用CompressBuf和DecompressBuf成功的情况下,调用者才能释放OutBuf返回的缓冲区。CompressBuf有一个缺点,就是它没有提供设置压缩方式的参数,总是使用Z_BEST_COMPRESSION(即clMax)方式压缩。不过只需要两行代码,我们可以改写它,加入压缩方式参数。以下是对这三个函数封装和改写后的单元(实际上只改写了CompressBuf函数,其它的都是直接抄写ZLib.pas,这么做只是为了使用的简便):
interface
uses
SysUtils ,
Classes ,
ZLib ,
ZLibConst;
const
CompressionLevels : array [ TCompressionLevel ] of ShortInt =
(
Z_NO_COMPRESSION ,
Z_BEST_SPEED ,
Z_DEFAULT_COMPRESSION ,
Z_BEST_COMPRESSION
);
procedure SfCompressBuf( const InBuf : Pointer; InBytes : Integer ; out OutBuf : Pointer;
out OutBytes : Integer ; Level : TCompressionLevel = clDefault);
procedure SfDecompressBuf( const InBuf : Pointer; InBytes : Integer ;
OutEstimate : Integer ; out OutBuf : Pointer; out OutBytes : Integer );
procedure SfDecompressToUserBuf( const InBuf : Pointer; InBytes : Integer ;
const OutBuf : Pointer; BufSize : Integer );
implementation
function CCheck( code : Integer ) : Integer ;
begin
Result := code;
if code < 0 then
raise ECompressionError . Create( sError );
end ;
function DCheck( code : Integer ) : Integer ;
begin
Result := code;
if code < 0 then
raise EDecompressionError . Create( sError );
end ;
procedure SfCompressBuf( const InBuf : Pointer; InBytes : Integer ;
out OutBuf : Pointer; out OutBytes : Integer ; Level : TCompressionLevel );
var
strm : TZStreamRec;
P : Pointer;
begin
FillChar( strm , sizeof ( strm ) , 0 );
strm . zalloc := zlibAllocMem;
strm . zfree := zlibFreeMem;
OutBytes := ( ( InBytes + ( InBytes div 10 ) + 12 ) + 255 ) and not 255 ;
GetMem( OutBuf , OutBytes );
try
strm . next_in := InBuf;
strm . avail_in := InBytes;
strm . next_out := OutBuf;
strm . avail_out := OutBytes;
CCheck( deflateInit_( strm , CompressionLevels[Level] , zlib_version , sizeof ( strm ) ) );
try
while CCheck( deflate( strm , Z_FINISH ) ) <> Z_STREAM_END do
begin
P := OutBuf;
Inc( OutBytes , 256 );
ReallocMem( OutBuf , OutBytes );
strm . next_out := PChar( Integer ( OutBuf ) + ( Integer ( strm . next_out ) - Integer ( P ) ) );
strm . avail_out := 256 ;
end ;
finally
CCheck( deflateEnd( strm ) );
end ;
ReallocMem( OutBuf , strm . total_out );
OutBytes := strm . total_out;
except
FreeMem( OutBuf );
raise;
end ;
end ;
procedure SfDecompressBuf( const InBuf : Pointer; InBytes : Integer ;
OutEstimate : Integer ; out OutBuf : Pointer; out OutBytes : Integer );
var
strm : TZStreamRec;
P : Pointer;
BufInc : Integer ;
begin
FillChar( strm , sizeof ( strm ) , 0 );
strm . zalloc := zlibAllocMem;
strm . zfree := zlibFreeMem;
BufInc := ( InBytes + 255 ) and not 255 ;
if OutEstimate = 0 then
OutBytes := BufInc
else
OutBytes := OutEstimate;
GetMem( OutBuf , OutBytes );
try
strm . next_in := InBuf;
strm . avail_in := InBytes;
strm . next_out := OutBuf;
strm . avail_out := OutBytes;
DCheck( inflateInit_( strm , zlib_version , sizeof ( strm ) ) );
try
while DCheck( inflate( strm , Z_FINISH ) ) <> Z_STREAM_END do
begin
P := OutBuf;
Inc( OutBytes , BufInc );
ReallocMem( OutBuf , OutBytes );
strm . next_out := PChar( Integer ( OutBuf ) + ( Integer ( strm . next_out ) - Integer ( P ) ) );
strm . avail_out := BufInc;
end ;
finally
DCheck( inflateEnd( strm ) );
end ;
ReallocMem( OutBuf , strm . total_out );
OutBytes := strm . total_out;
except
FreeMem( OutBuf );
raise
end ;
end ;
procedure SfDecompressToUserBuf( const InBuf : Pointer; InBytes : Integer ;
const OutBuf : Pointer; BufSize : Integer );
var
strm : TZStreamRec;
begin
FillChar( strm , sizeof ( strm ) , 0 );
strm . zalloc := zlibAllocMem;
strm . zfree := zlibFreeMem;
strm . next_in := InBuf;
strm . avail_in := InBytes;
strm . next_out := OutBuf;
strm . avail_out := BufSize;
DCheck( inflateInit_( strm , zlib_version , sizeof ( strm ) ) );
try
if DCheck( inflate( strm , Z_FINISH ) ) <> Z_STREAM_END then
raise EZlibError . CreateRes( @sTargetBufferTooSmall );
finally
DCheck( inflateEnd( strm ) );
end ;
end ;
end .
以下是使用这些函数进行数据压缩和解压的示例代码:
{压缩流}
function CompressStream(ASrcStream : TStream; ALevel : TSfCompressionLevel) : TStream;
var
SrcData , Buffer : Pointer;
BufSize : Integer ;
begin
Buffer := nil;
Result := nil;
BufSize := 0 ;
GetMem(SrcData , ASrcStream . Size);
ASrcStream . Position := 0 ;
ASrcStream . Read(SrcData ^, ASrcStream . Size);
try
try
SfCompressBuf(SrcData , ASrcStream . Size , Buffer , BufSize , ALevel);
except
on E : Exception do
SfRaiseException(E , ' Exception raised in CompressStream call ' );
end ;
finally
FreeMem(SrcData);
SrcData := nil;
end ;
// 由于try...except块中重引发了异常,所以在发生了异常的情况下,以下的代码不会执行
Result := TMemoryStream . Create;
Result . Write(Buffer ^, BufSize);
FreeMem(Buffer);
end ;
{解压流}
function CompressStream(ASrcStream : TStream; ALevel : TSfCompressionLevel) : TStream;
var
SrcData , Buffer : Pointer;
BufSize : Integer ;
begin
Buffer := nil;
Result := nil;
BufSize := 0 ;
GetMem(SrcData , ASrcStream . Size);
ASrcStream . Position := 0 ;
ASrcStream . Read(SrcData ^, ASrcStream . Size);
try
try
SfCompressBuf(SrcData , ASrcStream . Size , Buffer , BufSize , ALevel);
except
on E : Exception do
SfRaiseException(E , ' Exception raised in CompressStream call ' );
end ;
finally
FreeMem(SrcData);
SrcData := nil;
end ;
// 由于try...except块中重引发了异常,所以在发生了异常的情况下,以下的代码不会执行
Result := TMemoryStream . Create;
Result . Write(Buffer ^, BufSize);
FreeMem(Buffer);
end ;
{压缩字节数组}
function CompressBytes(ASrcBytes : TBytes; ALevel : TSfCompressionLevel) : TBytes;
var
Buffer : Pointer;
BufSize : Integer ;
begin
Buffer := nil;
BufSize := 0 ;
try
SfCompressBuf(@ASrcBytes[ 0 ] , Length(ASrcBytes) , Buffer , BufSize , ALevel);
SetLength(Result , BufSize);
Move(Buffer ^, Result[ 0 ] , BufSize);
except
on E : Exception do
SfRaiseException(E , ' Exception raised in CompressBytes call ' );
end ;
// 由于try...except块中重引发了异常,所以在发生了异常的情况下,以下的代码不会执行
FreeMem(Buffer);
end ;
{解压字节数组}
function DecompressBytes(ASrcBytes : TBytes) : TBytes;
var
Buffer : Pointer;
BufSize : Integer ;
begin
Buffer := nil;
BufSize := 0 ;
try
SfDecompressBuf(@ASrcBytes[ 0 ] , Length(ASrcBytes) , 0 , Buffer , BufSize);
SetLength(Result , BufSize);
Move(Buffer ^, Result[ 0 ] , BufSize);
except
on E : Exception do
SfRaiseException(E , ' Exception raised in DecompressBytes call ' );
end ;
// 由于try...except块中重引发了异常,所以在发生了异常的情况下,以下的代码不会执行
FreeMem(Buffer);
end ;
看了压缩和解压字节数组的代码就会明白,直接使用函数避免了构造对象和销毁对象的开销
ZLib的数据压缩和解压缩相关推荐
- 利用 API 实现数据压缩和解压缩
背景 对于Windows上的数据压缩和解压缩的实现,最方便的就是直接调用Win32 API函数.Widnows系统的ntdll.dll专门提供了RtlCompressBuffer函数和RtlDecom ...
- php zlib 解压缩,使用php的zlib压缩和解压缩swf文件
我在以前写过怎么使用c#来压缩和解压缩swf文件,解压缩,压缩和读取flash头文件信息需要使用一个开源的链接库,而且使用起来也不是很方便,但是使用php就不一样了,php包含了zlib的链接库,可以 ...
- java putnextentry_Java对zip格式压缩和解压缩
Java对zip格式压缩和解压缩 通过使用java的相关类可以实现对文件或文件夹的压缩,以及对压缩文件的解压. 1.1 ZIP和GZIP的区别 gzip是一种文件压缩工具(或该压缩工具产生的压缩文件格 ...
- 使用GZipStream实现压缩和解压缩
概述 之前做项目,涉及到存入到数据库或者http传输的数据量比较大,这个时候,就需要考虑在存入数据库或者发送传输之前,将数据压缩下,当从数据库中取出时,再解压还原数据.特地找了下发现有GZipStre ...
- Qt中用QuaZip来压缩和解压缩文件
1.简介 QuaZIP是使用Qt,C++对ZLIB进行简单封装的用于压缩ZIP以及解压缩ZIP的开源库.如果你的Qt项目当中用到了压缩以及解压缩ZIP的话你可以考虑选择使用它. 官方主页:http:/ ...
- EduCoder Linux之文件打包和解压缩
本实训主要讲解Linux中对文件/目录压缩和解压缩操作. 随着多媒体.视频图象.文档映象等技术的出现,数据压缩成了一个重要研究点.数据压缩基本上是挤压数据使得它占用更少的磁盘存储空间和更短的传输时间. ...
- python怎样压缩和解压缩ZIP文件
python怎样压缩和解压缩ZIP文件(转) 有时我们需要在 Python 中使用 zip 文件,而在1.6版中,Python 就已经提供了 zipfile 模块可以进行这样的操作.不过 Python ...
- python读压缩文件内容_使用Python读写及压缩和解压缩文件的示例
读写文件 首先看一个例子: f = open('thefile.txt','w') #以写方式打开, try: f.write('wokao') finally: f.close() 文件的打开方式: ...
- zipfile的压缩和解压缩
zipfile是python提供的内置的压缩方法 可以对zip文件进行压缩和解压缩 压缩: zf = ZipFile("out.zip", "w", mode= ...
最新文章
- STM32中printf重定向到串口
- php方法数组注释,php中的注释、变量、数组、常量、函数应用介绍
- 南开大学2014年高等代数部分试题解答
- python tuple类型,Python基础数据类型(四) tuple元祖
- easyUI tabs 显示与隐藏 tab 页
- Oracle中Cursor介绍
- 中国大学MOOC 人工智能导论第四章测试
- React Native 轻松集成分享功能( iOS 篇)
- 学游戏设计要什么学历_东莞模具设计培训学模具技术有什么好处为什么学模具技术...
- PAT (Basic Level) Practice1013 数素数
- Cascade EF-GAN: 局部聚焦渐进式面部表情编辑
- BIM轻量化技术解析
- Win11右键菜单改回Win10
- redis分布式方案redis cluster的介绍和实践
- 【CSDN竞赛第5期】编程竞赛总结
- 共有24款C/C++ 地理信息系统GIS开源软件
- 用Python助女神发朋友圈
- 错误 You've successfully authenticated, but Gitee.com does not provide she access.
- 这年头,能坐上火箭的东西不多啊,Java版本号算一个
- matlab 概率生成函数 求概率,已知某概率密度函数,如何产生一服从该分布的随机数...