一些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;


类别:Delphi 查看评论

转载于:https://www.cnblogs.com/dyz/archive/2010/01/26/1656354.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/462009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

easyUI 绑定右键菜单在数据行上显示

easyUI的显示数据的div都有一个样式,如下图 所有的表格都有一个datagrid-cell的样式那么这个时候我们就可以利用jquery来做时间的绑定了 下面是主要的js代码: /*绑定右键*/$(".datagrid-cell").live(contextmenu,function(e){//显示快捷菜单$(#mm).menu(show, {left:…

值传递与地址传递的区别

以下内容源于网络资源的学习与整理,欢迎交流。 总结 值传递,只是把原参的复制品传给形参,在子函数中修改这个形参,不会改变主函数中的原参。 地址传递,由于形参和原参表示同一个内容,在子函数中修改形参&a…

交叉编译器arm-linux-gcc

(一)交叉编译器简介 在一种计算机环境中运行的编译程序,能编译出在另外一种环境下运行的代码,这个编译过程就叫交叉编译.简单地说,就是在一个平台上生成另一个平台上的可执行代码. (二)体系结构与操作系统 (1)常见的体系结构有ARM结构、x86结…

web前端细解cookie那些事

web前端细解cookie那些事,在互联网时代,IT行业飞速发展,带动了web前端开发行业的兴趣。由于行业新兴起时间不久,专业人才缺乏,薪资待遇较高,已成为众多IT学子选择就业的首选,今天就为分享一些有…

一个很好的机器学习普及网站

1.介绍svm等算法深入浅出的网站 Free Mind 2.关于聚类算法的普及 A tutorial on Clustering Algorithm(中文翻译) 模糊聚类的应用--目标分割 exlusive clustering(比如K-means) hierarchy clustering:先将 n 个样品各看成一类, 然后规定类与…

字符串处理示例--列车车次查询.sql

--列车车次信息数据表CREATE TABLE tb(col varchar(100))INSERT tb SELECT 1434/1/2/14UNION ALL SELECT "10653(85707)"UNION ALL SELECT "32608/7(83212/1)"UNION ALL SELECT "50057()"UNION ALL SELECT "T888(备&…

查找算法的总结

声明:以下内容来源于网络资料的学习和整理 一、查找技术分类 1、静态查找表技术(顺序查找、二分查找、分块查找) 2、动态查找表技术(二叉查找树) 3、哈希表技术(哈希表技术) 二、查找技术说明…

一个C程序的编译过程(Linux环境下Gcc)

一 以下是C程序一般的编译过程: 从图中看到: 将编写的一个c程序(源代码 )转换成可以在硬件上运行的程序(可执行代码 ),需要进行编译阶段 和链接这两个阶段。 其中, 1. 编译阶段先通…

python3 django连接mysql 数据库

详情参考: https://blog.csdn.net/weixin_33127753/article/details/89100552 https://imshusheng.com/python/216.html 报错环境 python3.6,django2.2,PyMySQL0.9.3……django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or…

redis 主从复制 [转]

一、Redis的Replication: 这里首先需要说明的是,在Redis中配置Master-Slave模式真是太简单了。相信在阅读完这篇Blog之后你也可以轻松做到。这里我们还是先列出一些理论性的知识,后面给出实际操作的案例。 下面的列表清楚的解释了Redis…

动态查找表之二叉搜索树

一、二叉搜索树(BST) 二叉搜索树(二叉排序树)定义如下: (1)一棵空树; (2)或者不是空树: 1)若左子树不空,则左子树上所有…

Mysql数据导入导出

导出导入数据库导出mysqldump方法mysqldump -u用户名 -p密码名 database [table]> 目标文件导入mysql -uroot -prootuse databasesource 目标文件;PS: 这种方法是导出整个表数据,并且带着建表信息,假如导入的数据库有同名的表,…

2-10 就业课(2.0)-oozie:9、oozie与hue的整合,以及整合后执行MR任务

5、hue整合oozie 第一步:停止oozie与hue的进程 通过命令停止oozie与hue的进程,准备修改oozie与hue的配置文件 第二步:修改oozie的配置文件(老版本的bug,新版本已经不需要了)这一步我们都不需要做了 修改ooz…

Screen Painter 程序设计

一、Screen 的创建及维护, TCode:SE51 输入程序名称,单击【建立】, 程序1000为SAP预留屏幕号,屏幕号必须定义1000外的其他数字,且最多不超过四位, 本例定义屏幕为SAP预留屏幕号为:100 * 属性设置&#xff1…

完全图解VS2017安装过程并演示VS2017创建Linux项目和调试

VS2017个人免费版即社区官方下载地址为:https://download.microsoft.com/download/D/1/4/D142F7E7-4D7E-4F3B-A399-5BACA91EB569/vs_Community.exe 这是一个很小的在线下载安装器。VS2017安装变得人性化了,根据组件的分类,供安装用户选择&…

spring--打印hello--注解component--自动创建对象

1.创建 GroupId----项目目录&#xff08;com.javaspring&#xff09; Artifactid---项目名称(spring01qiuckstart) Version--版本默认 2.默认打开的pom.xml文件 编辑---编写spring核心项目依赖 <?xml version"1.0" encoding"UTF-8"?> <project …

菜鸟学数据库(四)——超键、候选键、主键、外键

这些年的一些经历告诉我&#xff0c;很多初学者搞不清超键、候选键等&#xff0c;被数据库中的各种键搞的一头雾水。下面就跟大家一起聊聊数据库中的那些键。 首先看看各种键的定义&#xff1a; 超键(super key):在关系中能唯一标识元组的属性集称为关系模式的超键 候选键(ca…

嵌入式C语言之struct内存分配分析

本文源于微信号《嵌入式ARM》。链接&#xff1a;http://mp.weixin.qq.com/s/j2mk6jY79nrJge2cDZVH_A 对结构MyStruct采用sizeof会出现什么结果呢&#xff1f;sizeof(MyStruct)为多少呢&#xff1f; 也许你会这样求&#xff1a;sizeof(MyStruct)sizeof(double)sizeof(char)sizeo…

open和fopen的区别

open和fopen的区别&#xff1a; 1.非缓冲文件系统缓冲文件系统是借助文件结构体指针来对文件进行管理&#xff0c;通过文件指针来对文件进行访问&#xff0c;既可以读写字符、字符串、格式化数据&#xff0c;也可以读写二进制数 据。非缓冲文件系统依赖于操作系统&#xff0c;通…

PostMessage发送字符串和结构体

2019独角兽企业重金招聘Python工程师标准>>> 1.首先定义消息变量 #define WM_POST_MSG WM_USER 2 2.增加消息处理函数 afx_msg LRESULT UpdateStatic(WPARAM wParam, LPARAM lParam); 3.增加消息映射 ON_MESSAGE(WM_POST_MSG,UpdateStatic) 当调用PostMessage函…