Delphi的开发者可以使用ZLib单元中定义的TCompressionStream进行数据压缩,使用TDecompressionStream解压缩ZLib压缩后的数据。它们的定义如下:

TCustomZlibStream  =   class (TStream)
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对象中包含了压缩结果数据}

function  CompressStream(ASrcStream :  TStream; ALevel :  TSfCompressionLevel) :  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 CompressBuf( const  InBuf :  Pointer; InBytes :   Integer ;out OutBuf :  Pointer; out OutBytes :   Integer );

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,这么做只是为了使用的简便):

unit SfCompressUtils;

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的数据压缩和解压缩相关推荐

  1. 利用 API 实现数据压缩和解压缩

    背景 对于Windows上的数据压缩和解压缩的实现,最方便的就是直接调用Win32 API函数.Widnows系统的ntdll.dll专门提供了RtlCompressBuffer函数和RtlDecom ...

  2. php zlib 解压缩,使用php的zlib压缩和解压缩swf文件

    我在以前写过怎么使用c#来压缩和解压缩swf文件,解压缩,压缩和读取flash头文件信息需要使用一个开源的链接库,而且使用起来也不是很方便,但是使用php就不一样了,php包含了zlib的链接库,可以 ...

  3. java putnextentry_Java对zip格式压缩和解压缩

    Java对zip格式压缩和解压缩 通过使用java的相关类可以实现对文件或文件夹的压缩,以及对压缩文件的解压. 1.1 ZIP和GZIP的区别 gzip是一种文件压缩工具(或该压缩工具产生的压缩文件格 ...

  4. 使用GZipStream实现压缩和解压缩

    概述 之前做项目,涉及到存入到数据库或者http传输的数据量比较大,这个时候,就需要考虑在存入数据库或者发送传输之前,将数据压缩下,当从数据库中取出时,再解压还原数据.特地找了下发现有GZipStre ...

  5. Qt中用QuaZip来压缩和解压缩文件

    1.简介 QuaZIP是使用Qt,C++对ZLIB进行简单封装的用于压缩ZIP以及解压缩ZIP的开源库.如果你的Qt项目当中用到了压缩以及解压缩ZIP的话你可以考虑选择使用它. 官方主页:http:/ ...

  6. EduCoder Linux之文件打包和解压缩

    本实训主要讲解Linux中对文件/目录压缩和解压缩操作. 随着多媒体.视频图象.文档映象等技术的出现,数据压缩成了一个重要研究点.数据压缩基本上是挤压数据使得它占用更少的磁盘存储空间和更短的传输时间. ...

  7. python怎样压缩和解压缩ZIP文件

    python怎样压缩和解压缩ZIP文件(转) 有时我们需要在 Python 中使用 zip 文件,而在1.6版中,Python 就已经提供了 zipfile 模块可以进行这样的操作.不过 Python ...

  8. python读压缩文件内容_使用Python读写及压缩和解压缩文件的示例

    读写文件 首先看一个例子: f = open('thefile.txt','w') #以写方式打开, try: f.write('wokao') finally: f.close() 文件的打开方式: ...

  9. zipfile的压缩和解压缩

    zipfile是python提供的内置的压缩方法 可以对zip文件进行压缩和解压缩 压缩: zf = ZipFile("out.zip", "w", mode= ...

最新文章

  1. STM32中printf重定向到串口
  2. php方法数组注释,php中的注释、变量、数组、常量、函数应用介绍
  3. 南开大学2014年高等代数部分试题解答
  4. python tuple类型,Python基础数据类型(四) tuple元祖
  5. easyUI tabs 显示与隐藏 tab 页
  6. Oracle中Cursor介绍
  7. 中国大学MOOC 人工智能导论第四章测试
  8. React Native 轻松集成分享功能( iOS 篇)
  9. 学游戏设计要什么学历_东莞模具设计培训学模具技术有什么好处为什么学模具技术...
  10. PAT (Basic Level) Practice1013 数素数
  11. Cascade EF-GAN: 局部聚焦渐进式面部表情编辑
  12. BIM轻量化技术解析
  13. Win11右键菜单改回Win10
  14. redis分布式方案redis cluster的介绍和实践
  15. 【CSDN竞赛第5期】编程竞赛总结
  16. 共有24款C/C++ 地理信息系统GIS开源软件
  17. 用Python助女神发朋友圈
  18. 错误 You've successfully authenticated, but Gitee.com does not provide she access.
  19. 这年头,能坐上火箭的东西不多啊,Java版本号算一个
  20. matlab 概率生成函数 求概率,已知某概率密度函数,如何产生一服从该分布的随机数...

热门文章

  1. H5页面在ios的浏览器上使用 高德地图 报当前定位失败Geolocation permission denied 或者 偶尔报AMap没有找到的
  2. Formatter格式化
  3. 基于因果推断的根因分析
  4. 运放--单电源半波精密整流
  5. 区块链难理解?这里有一篇初学者指南
  6. 同一交换机不同网段的主机间通信
  7. 干货 | 你的 KL 散度 vanish 了吗?
  8. 事业单位招聘计算机类面试自我介绍,事业单位面试自我介绍范文2分钟|2019事业单位面试自我介绍范文...
  9. RIO10数据集下载链接提取
  10. 最全智能制造数字化应用方案