zoukankan      html  css  js  c++  java
  • delphi Base64编码/解码及数据压缩/解压知识

    一.Base64编码/解码

      一般用到的是Delphi自带的单元EncdDecd,当然还有第三方提供的单元或控件,其中我所接触到的认为比较好的有Indy的TIdMimeEncode / TIdMimeDecode组件,以及RjMime单元.

      在这里主要想讲讲如何才能获得最好的编码/解码性能,EncdDecd提供了EncodeStream/DecodeString, EncodeString/DecodeString两对函数,如果你使用EncodeString/DecodeString,这没有什麽可争议,效率是死的,如果你使用了EncodeStream/DecodeStream,这里面可大有文章了. 先来看看两个函数的声明:

    procedure EncodeStream(Input, Output: TStream);

    procedure DecodeStream(Input, Output: TStream);

      很明了, 两个参数,都为TStream, TStream是抽象类, 其派生类主要有TMomoryStream,TStringStream,TFileStream等,都可以作为参数传递进去,对於Input参数,无论TMemoryStream, TStringStream, TFileStream都不会影响性能,但是对於Output参数,由於压缩的结果是写住OutputStream,因此压缩过程中不断地执行TStream的Write方法,如果是TMemoryStream,那效率真是太糟糕了,我作过测试,编码一个5M多的文件,要十几秒钟!但如果是TStringStream呢,只要0.2~0.3秒! 这究竟是为什麽呢,因为TMemoryStream里不断调用Write方法的结果是,不断地向Windows要求分配内存!从而导致性能下降!而TStringStream和TFileStream则没有这个问题. 因此,在这里极力向朋友们建议,Output参数最好不用TMemoryStream.

      不过不要紧,你一定要用的话,也是有方法解决性能下降这个问题的! 因为效率下降的原因是不断的申请内存空间,我们可以从这个方向首手,能不能一次性给它分配好内存空间呢,如果我们事先能确定编码或解码后的数据大小(字节数),那麽这是可行的. 问题的关键就是如何确定这个编码或解码后的字节数了. 对於EncdDecd,我可以给出这个计算方法:

      (1)假设编码前的字节数为X,那麽编码后的字节数为 (X + 2) div 3 * 4. 不过,要对EncdDecd进行相应的修改,找到这一小段:

       if K > 75 then     

       begin

        BufPtr[0] := #$0D;

        BufPtr[1] := #$0A;

        Inc(BufPtr, 2);

        K := 0;

       end;

      将其注释掉, 因为这其实是没什麽用的,只是用来对编码后的字符串分行的~,我们可以注释后将单元另存为EncdDecdEx,以后就使用它了!!!

      (2)假设解码前的字节数是X,那麽解码后的字节数约为 (X + 3) div 4 * 3

    *****注:与编码不同的是,解码的字节数不是确定的,差值在0~2之间.

      这样我们就可以在编码/解码前对Output参数的TMemoryStream事先设置缓冲区大小了....

     

     uses

      encddecdEx;

     var

      Input,Output:TMemoryStream;

     begin

      Input:=TMemoryStream.Create;

      try

       Input.LoadFromFile('c:aaa.txt');

       Output:=TMemoryStream.Create;

       try

        Output.Size:=(Input.Size + 2) div 3 * 4;

        EncodeStream(Input,Output);

       finally

        Output.Free;

       end;

      finally

       Input.Free;

      end;

     end;

     OK! 大功告成!!! 大家有兴趣可以测试一下,加不加Output.Size:=(Input.Size + 2) div 3 * 4这一句的不同效果~

    二.ZLib压缩/解压

      在一些分布式系统中,特别是Internet分布式系统,由於网络带宽所限,我们需要对传输的大流量数据进行压缩,以减轻网络的负担,加快程序运行速度,一般用到的压缩/解压方法是使用ZLib单元. ZLib单元主要提供了两个类:TCompressionStream和TDeCompressionStream. 这两个类分别处理压缩和解压缩. 使用方法可以查阅相关的资料. 在这里提供两个过程,再对压缩时的参数作些比较:

    uses

     ZLib;

    procedure Zip(Input,Output:TStream;Compress:Boolean);

    const

     MAXBUFSIZE=1024 * 16;  //16 KB

    var

     CS:TCompressionStream;

     DS:TDecompressionStream;

     Buf:array[0..MAXBUFSIZE-1] of Byte;

     BufSize:Integer;

    begin

      if Assigned(Input) and Assigned(Output) then

     begin

      if Compress then

      begin

       CS:=TCompressionStream.Create(clDefault,Output);

       try

        CS.CopyFrom(Input,0); //从开始处复制

       finally

        CS.Free;

       end;

      end else

      begin

       DS:=TDecompressionStream.Create(Input);

       try

        BufSize:=DS.Read(Buf,MAXBUFSIZE);

        while BufSize>0 do

        begin

         Output.Write(Buf,BufSize);

         BufSize:=DS.Read(Buf,MAXBUFSIZE);

        end;

       finally

        DS.Free;

       end;

      end;

     end;

    end;

    function Zip(Input:string;Compress:Boolean):string;

    var

     InputStream,OutputStream:TStringStream;

    begin

     if Input='' then Exit;

     InputStream:=TStringStream.Create(Input);

     try

      OutputStream:=TStringStream.Create('');

      try

       Zip(InputStream,OutputStream,Compress);

       Result:=OutputStream.DataString;

      finally

       OutputStream.Free;

      end;

     finally

      InputStream.Free;

     end;

    end;

      以上两个方法是两个名称一样,参数不同的过程. 第一个是对流进行压缩/解压,Input,Output分别是压缩/解压前的流与压缩/解压后的流. 第二个是对字符串进行压缩/解压. 两个过程都有Compress参数,这个参数用来决定进行压缩操作还是解压操作: True--压缩; false--解压.

      在第一个过程中,有这样一句:

      CS:=TCompressionStream.Create(clDefault,Output);

      这是在建立压缩类以对流进行压缩, 这里面有个参数clDefault,当然还有其它的选项:clNone, clFastest, clDefault, clMax;

    clNone与clFastest就不讨论了,因为不能获得良好的压缩效果,在这里想讨论clDeafult与clMax哪一个好点,我作了一些测试,测试数据如下:


            源文件大小  压缩所用时间   压缩后文件大小

     clDefault   2.71M     ~1.4s      ~937K

             5.10M     ~2.8s      ~1.77M

     clMax     2.71M     ~2.5s      ~934K

             5.10M     ~4.7s      ~1.77M

      由这些数据可以看出,clDefault参数与clMax参数,压缩率已经非常接近了,但是所用的时间却相差了近一倍! 也就是说,差不多的压缩效率,clDefault参数比clMax参数节省了一半的时间! 因此,建议大家使用参数clDefault,这是压缩效率比较好的参数.


    三. 何对MIDAS封包进行压缩.

      我们知道,MIDAS封包外在类型是OleVariant,其内部格式没有对外公开! 经过我的一些测试,该封包是以varByte为基础类型的VarArray数组.

    因此,我们可以将其转换成string类型再进行压缩,至於压缩后是以string传输还是转换回VarByte array类型,就由个人决定了. 下面的函数完成将MIDAS数据包转换成string类型.

    function UnpackMIDAS(vData:OleVariant):string;

    var

     P:Pointer;

     Size:Integer;

    begin

     if not VarIsArray(vData) then Exit;

     Size:=VarArrayHighBound(vData,1)-VarArrayLowBound(vData,1)+1;

     P:=VarArrayLock(vData);

     try

      SetLength(Result,Size);

      Move(P^,Result[1],Size);

     finally

      VarArrayUnLock(vData);

     end;

    end;

    假设以下为MIDAS服务器或COM+对象一个方法.

    function TDeptCoor.GetDeptData: OleVariant;

    var

     Command:WideString;

     Options:TGetRecordOptions;

     RecsOut:Integer;

     Params,OwnerData:OleVariant;

    begin

     try

      Command:='SELECT DeptID,DeptNo,DeptName,MasterID FROM Department ORDER BY DeptNo';

      Options:=[grReset,grMetaData];

      Result:=FCommTDM.AS_GetRecords('CommDsp',-1,RecsOut,Byte(Options),Command,Params,OwnerData);

      Result:=UnpackMIDAS(Result);  //将MIDAS封包转换成string类型

      Result:=Zip(Result,True);      //进行压缩,再将压缩后结果转回.

      SetComplete;

     except

      SetAbort;

      raise;

     end;

    end;

    客户端只要压压缩后就可以使用了:

    procedure TForm1.Button1Click(sender:TObject);

    var

     vData:string;

    begin

     vData:=DeptCoor.GetDeptData;

     vData:=Zip(vData,False);     //解压

     ClientDataSet1.XMLData:=vData;  //注意,这里用的是XMLData,不是Data,否则会报错!!!

    end;

            

    四. SOAP系统中压缩后编码:

     在SOAP系统中,由於二进制数据不能直接传递,需要进行Base64编码, 我们可以在数据传递前先压缩/Base64编码,接收后再Base64解码/解压缩.

    同样,也给出两个函数,来分别完成这两个过程

    function SoapPacket(const Input:string):string; 

    var

     InputStream,OutputStream:TStringStream;

    begin

     InputStream:=TStringStream.Create(Input);

     try

      OutputStream:=TStringStream.Create('');

      try

       Zip(InputStream,OutputStream,True);

       InputStream.Size:=0;

       OutputStream.Position:=0;  //很重要!!!

       EncodeStream(OutputStream,InputStream);

       Result:=InputStream.DataString;

      finally

       OutputStream.Free;

      end;

     finally

      InputStream.Free;

     end;

    end;

    function SoapUnpack(const Input:string):string;

    var

     InputStream,OutputStream:TStringStream;

    begin

     InputStream:=TStringStream.Create(Input);

     try

      OutputStream:=TStringStream.Create('');

      try

       DecodeStream(InputStream,OutputStream);

       InputStream.Size:=0;

       OutputStream.Position:=0; //很重要!!!

       Zip(OutputStream,InputStream,False);

       Result:=InputStream.DataString;

      finally

       OutputStream.Free;

      end;

     finally

      InputStream.Free;

     end;

    end;

  • 相关阅读:
    批量重命名工具 Bulk Rename Utility
    Makefile中的ifeq 多条件使用
    利用Python批量下载邮件附件
    在线随机抽取工具、在线汉字转拼音
    《如何把事情做到最好》读书笔记
    Android 通过adb快速恢复出厂设置
    Makefile的ifeq逻辑或,逻辑与的变通实现
    Android 获取后台正在运行的第三方应用列表
    Android.mk 中 filter 和 filterout 的用法
    Android TV端电视直播软件 和 投屏工具
  • 原文地址:https://www.cnblogs.com/FuYan/p/4570280.html
Copyright © 2011-2022 走看看