在 Delphi 中解压 DeflateStream (C#)
Decompress DeflateStream (C#) in Delphi
在我的应用程序中,我构建了一个 xml 结构并将其发送给 delphi 客户端。在 xml 的标签中,我有一个压缩的 base64 编码字符串:
public static string Zip(string text)
{
byte[] buffer = System.Text.Encoding.Unicode.GetBytes(text);
MemoryStream ms = new MemoryStream();
//using (System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress, true))
//{
// zip.Write(buffer, 0, buffer.Length);
//}
using (System.IO.Compression.DeflateStream zip = new System.IO.Compression.DeflateStream(ms, System.IO.Compression.CompressionMode.Compress, true))
{
zip.Write(buffer, 0, buffer.Length);
}
ms.Position = 0;
MemoryStream outStream = new MemoryStream();
byte[] compressed = new byte[ms.Length];
ms.Read(compressed, 0, compressed.Length);
byte[] gzBuffer = new byte[compressed.Length + 4];
System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
return Convert.ToBase64String(gzBuffer);
}
我的 Delphi 客户端必须从该标签中获取数据并将其再次转换为基字符串。不幸的是,我得到了
ezdecompressionerror data error
我尝试了一些网络提供的功能,例如:
function ZDecompressString(aText: string): string;
var
Utf8Stream: TStringStream;
Compressed: TMemoryStream;
Base64Stream: TStringStream;
begin
Base64Stream := TStringStream.Create(aText, TEncoding.ASCII);
try
Compressed := TMemoryStream.Create;
try
DecodeStream(Base64Stream, Compressed);
Compressed.Position := 0;
Utf8Stream := TStringStream.Create('', TEncoding.ANSI);
try
ZDecompressStream(Compressed, Utf8Stream);
Result := Utf8Stream.DataString;
finally
Utf8Stream.Free;
end;
finally
Compressed.Free;
end;
finally
Base64Stream.Free;
end;
end;
但这里没有任何效果。我正在使用 XE2 和标准 Zlib 库。我阅读了一些文章,但我无法弄清楚:
http://forum.codecall.net/topic/76077-compress-and-decompress-with-zlib-library/
http://www.yanniel.info/2011/01/string-compress-decompress-delphi-zlib.html
Delphi XE and ZLib Problems
http://www.delphipraxis.net/89090-string-mit-gzip-ent-zippen.html
我也试过用 c# 解压缩它,应该不会对它起作用感到惊讶。我想我的问题出在对delphi解压代码的理解上,或者我是一个真正的笨蛋。但不幸的是,我不明白我该如何完成这项工作。 :[
TIA
我将 re-write 两个代码块。我建议您使用 UTF-8 作为编码。对于大多数西方文本,它是最 space 高效的 Unicode 编码。
C# 代码如下所示:
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
public static string Zip(string text)
{
byte[] utf8bytes = System.Text.Encoding.UTF8.GetBytes(text);
MemoryStream compressedStream = new MemoryStream();
using (var gzipStream = new GZipStream(compressedStream,
CompressionMode.Compress, true))
{
gzipStream.Write(utf8bytes, 0, utf8bytes.Length);
}
compressedStream.Position = 0;
byte[] deflated = new byte[compressedStream.Length];
compressedStream.Read(deflated, 0, (int)compressedStream.Length);
return Convert.ToBase64String(deflated);
}
static void Main(string[] args)
{
Console.WriteLine(Zip("fubar"));
Console.ReadLine();
}
}
}
产生此输出:
H4sIAAAAAAAEAEsrTUosAgDmcA8FBQAAAA==
我基本上保留了您使用的相同代码,但切换到 UTF-8 并简化了代码,删除了一些不必要的步骤。我还删除了压缩缓冲区长度的写入。我认为没有必要,而且无论如何它都不遵守网络字节顺序。
更重要的是,我切换到 GZIP,因为在 Delphi 代码中更容易阅读。使用 deflate 会迫使你进入原始的 zlib 编程,这有点混乱。使用 GZIP 添加 GZIP header 到压缩流。
在 Delphi 端,代码如下所示:
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Classes,
System.ZLib,
Soap.EncdDecd;
function Unzip(const zipped: string): string;
var
DecompressionStream: TDecompressionStream;
Compressed: TBytesStream;
Decompressed: TStringStream;
begin
Compressed := TBytesStream.Create(DecodeBase64(AnsiString(zipped)));
try
// window bits set to 15 + 16 for gzip
DecompressionStream := TDecompressionStream.Create(Compressed, 15 + 16);
try
Decompressed := TStringStream.Create('', TEncoding.UTF8);
try
Decompressed.LoadFromStream(DecompressionStream);
Result := Decompressed.DataString;
finally
Decompressed.Free;
end;
finally
DecompressionStream.Free;
end;
finally
Compressed.Free;
end;
end;
procedure Main;
begin
Writeln(Unzip('H4sIAAAAAAAEAEsrTUosAgDmcA8FBQAAAA=='));
end;
begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
当然,对于小字符串,压缩开销和 GZIP header 意味着这不是压缩。加上 base64 编码,压缩+编码的字符串比输入长得多。
不过,我假设您希望发送大量文本,在这种情况下 GZIP header 将不重要。
在我的应用程序中,我构建了一个 xml 结构并将其发送给 delphi 客户端。在 xml 的标签中,我有一个压缩的 base64 编码字符串:
public static string Zip(string text)
{
byte[] buffer = System.Text.Encoding.Unicode.GetBytes(text);
MemoryStream ms = new MemoryStream();
//using (System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress, true))
//{
// zip.Write(buffer, 0, buffer.Length);
//}
using (System.IO.Compression.DeflateStream zip = new System.IO.Compression.DeflateStream(ms, System.IO.Compression.CompressionMode.Compress, true))
{
zip.Write(buffer, 0, buffer.Length);
}
ms.Position = 0;
MemoryStream outStream = new MemoryStream();
byte[] compressed = new byte[ms.Length];
ms.Read(compressed, 0, compressed.Length);
byte[] gzBuffer = new byte[compressed.Length + 4];
System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
return Convert.ToBase64String(gzBuffer);
}
我的 Delphi 客户端必须从该标签中获取数据并将其再次转换为基字符串。不幸的是,我得到了
ezdecompressionerror data error
我尝试了一些网络提供的功能,例如:
function ZDecompressString(aText: string): string;
var
Utf8Stream: TStringStream;
Compressed: TMemoryStream;
Base64Stream: TStringStream;
begin
Base64Stream := TStringStream.Create(aText, TEncoding.ASCII);
try
Compressed := TMemoryStream.Create;
try
DecodeStream(Base64Stream, Compressed);
Compressed.Position := 0;
Utf8Stream := TStringStream.Create('', TEncoding.ANSI);
try
ZDecompressStream(Compressed, Utf8Stream);
Result := Utf8Stream.DataString;
finally
Utf8Stream.Free;
end;
finally
Compressed.Free;
end;
finally
Base64Stream.Free;
end;
end;
但这里没有任何效果。我正在使用 XE2 和标准 Zlib 库。我阅读了一些文章,但我无法弄清楚:
http://forum.codecall.net/topic/76077-compress-and-decompress-with-zlib-library/
http://www.yanniel.info/2011/01/string-compress-decompress-delphi-zlib.html
Delphi XE and ZLib Problems
http://www.delphipraxis.net/89090-string-mit-gzip-ent-zippen.html
我也试过用 c# 解压缩它,应该不会对它起作用感到惊讶。我想我的问题出在对delphi解压代码的理解上,或者我是一个真正的笨蛋。但不幸的是,我不明白我该如何完成这项工作。 :[
TIA
我将 re-write 两个代码块。我建议您使用 UTF-8 作为编码。对于大多数西方文本,它是最 space 高效的 Unicode 编码。
C# 代码如下所示:
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
public static string Zip(string text)
{
byte[] utf8bytes = System.Text.Encoding.UTF8.GetBytes(text);
MemoryStream compressedStream = new MemoryStream();
using (var gzipStream = new GZipStream(compressedStream,
CompressionMode.Compress, true))
{
gzipStream.Write(utf8bytes, 0, utf8bytes.Length);
}
compressedStream.Position = 0;
byte[] deflated = new byte[compressedStream.Length];
compressedStream.Read(deflated, 0, (int)compressedStream.Length);
return Convert.ToBase64String(deflated);
}
static void Main(string[] args)
{
Console.WriteLine(Zip("fubar"));
Console.ReadLine();
}
}
}
产生此输出:
H4sIAAAAAAAEAEsrTUosAgDmcA8FBQAAAA==
我基本上保留了您使用的相同代码,但切换到 UTF-8 并简化了代码,删除了一些不必要的步骤。我还删除了压缩缓冲区长度的写入。我认为没有必要,而且无论如何它都不遵守网络字节顺序。
更重要的是,我切换到 GZIP,因为在 Delphi 代码中更容易阅读。使用 deflate 会迫使你进入原始的 zlib 编程,这有点混乱。使用 GZIP 添加 GZIP header 到压缩流。
在 Delphi 端,代码如下所示:
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Classes,
System.ZLib,
Soap.EncdDecd;
function Unzip(const zipped: string): string;
var
DecompressionStream: TDecompressionStream;
Compressed: TBytesStream;
Decompressed: TStringStream;
begin
Compressed := TBytesStream.Create(DecodeBase64(AnsiString(zipped)));
try
// window bits set to 15 + 16 for gzip
DecompressionStream := TDecompressionStream.Create(Compressed, 15 + 16);
try
Decompressed := TStringStream.Create('', TEncoding.UTF8);
try
Decompressed.LoadFromStream(DecompressionStream);
Result := Decompressed.DataString;
finally
Decompressed.Free;
end;
finally
DecompressionStream.Free;
end;
finally
Compressed.Free;
end;
end;
procedure Main;
begin
Writeln(Unzip('H4sIAAAAAAAEAEsrTUosAgDmcA8FBQAAAA=='));
end;
begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
当然,对于小字符串,压缩开销和 GZIP header 意味着这不是压缩。加上 base64 编码,压缩+编码的字符串比输入长得多。
不过,我假设您希望发送大量文本,在这种情况下 GZIP header 将不重要。