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;
delphi Base64编码/解码及数据压缩/解压知识的更多相关文章
- Delphi Base64编码/解码及ZLib压缩/解压
最近在写的程序与SOAP相关,所以用到了一些Base64编码/解码及数据压缩/解压方面的知识. 在这里来作一些总结: 一.Base64编码/解码 一般用到的是Delphi自带的单元EncdDe ...
- Delphi Base64编码/解码
Uses CnBase64: CnBase64.Base64Encode(Edit1.Text, Psw64);
- Delphi Base64编码_解码及ZLib压缩_解压(转)
最近在写的程序与SOAP相关,所以用到了一些Base64编码/解码及数据压缩/解压方面的知识. 在这里来作一些总结:一.Base64编码/解码 一般用到的是Delphi自带的单元EncdDecd,当然 ...
- OpenSSL 使用 base64 编码/解码
简述 关于 OpenSSL 的介绍及安装请参见:Windows下编译OpenSSL 下面主要介绍有关 OpenSSL 使用 base64 编码/解码. 简述 编码解码 更多参考 编码/解码 #incl ...
- 利用openssl进行BASE64编码解码、md5/sha1摘要、AES/DES3加密解密
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...
- Javascript中Base64编码解码的使用实例
Javascript为我们提供了一个简单的方法来实现字符串的Base64编码和解码,分别是window.btoa()函数和window.atob()函数. 1 var encodedStr = win ...
- Atitit. 二进制数据ascii表示法,与base64编码解码api 设计标准化总结java php c#.net
Atitit. 二进制数据ascii表示法,与base64编码解码api 设计标准化总结java php c#.net 1. Base64编码, 1 1.1. 子模式 urlsafe Or url ...
- Atitit. 二进制数据ascii表示法,与base64编码解码api 设计标准化总结java php c#.net
Atitit. 二进制数据ascii表示法,与base64编码解码api 设计标准化总结java php c#.net 1. Base64编码,1 1.1. 子模式 urlsafe Or url u ...
- Code:Base64 编码/解码
ylbtech-Code:Base64 编码/解码 1. C#返回顶部 1.编码 byte[] inArray = new byte[msgTxt.Length]; int x; ; x < m ...
随机推荐
- ASP.NET MVC 实现 AJAX 跨域请求
ASP.NET MVC 实现AJAX跨域请求的两种方法 和大家分享下Ajax 跨域的经验,之前也找了好多资料,但是都不行,后来看到个可行的修改了并测试下 果然OK了 希望对大家有所帮助! 通常发送 ...
- Codeforces Round #450 (Div. 2) B. Position in Fraction【数论/循环节/给定分子m 分母n和一个数c,找出c在m/n的循环节第几个位置出现,没出现过输出-1】
B. Position in Fraction time limit per test 1 second memory limit per test 256 megabytes input stand ...
- Educational Codeforces Round 39 (Rated for Div. 2) B. Weird Subtraction Process[数论/欧几里得算法]
https://zh.wikipedia.org/wiki/%E8%BC%BE%E8%BD%89%E7%9B%B8%E9%99%A4%E6%B3%95 取模也是一样的,就当多减几次. 在欧几里得最初的 ...
- 洛谷 P1618 三连击(升级版)【DFS/next_permutation()/技巧性枚举/sprintf】
[链接]:https://www.luogu.org/problemnew/show/P1618 题目描述 将1,2,…,9共9个数分成三组,分别组成三个三位数,且使这三个三位数的比例是A:B:C,试 ...
- mysql控制台入门级--简单的创建表,字段。。。(用于网站测试)
一:在Mysql控制台创建数据表 [sql] use ceshi; create table student ( stuid int primary key auto_incremen ...
- [转]使用Wireshark来检测一次HTTP连接过程
Wireshark是一个类似tcpdump的嗅探软件,界面更人性化一些,今天我用它来检测一次HTTP连接过程. 安装好之后,先配置一下,选择Capture->Options,先设定你要嗅探的网络 ...
- SpringMVC Controller 介绍【转】
链接地址: http://haohaoxuexi.iteye.com/blog/1753271
- CentOS-7 在windows server 2012下的虚拟机安装教程
CentOS-7 在windows server 2012下的虚拟机安装教程 一.下载 CentOS-7-x86_64-DVD-1611.iso https://mirrors.aliyun.com/ ...
- UE把环境变量Path改了
为了比较个文件,装了UE. 文件比较完了,环境变量也被改了. 改还不是写添加式的改,是写覆盖式的改. 搞得ant都起不动了,一看Path被改的那样(C:\hy\soft\ultraedit\Ultra ...
- 怎样mac上安装apk到连接数据线的手机
高大上的mac俺也用了一段时间了.不知道大家有木有同一个烦恼.曾经在win上的时候仅仅要安装了应用宝之类的手机助手.就能够双击APK,直接安装到连接数据线的手机上,非常方便哈,可是mac上不行.近期找 ...