如何使用与 .Net 兼容的 GZIPOutputStream 压缩和解压缩字符串?
我需要一个在 android 中使用 GZip 压缩字符串的示例。我想向该方法发送一个像“hello”这样的字符串并获取以下压缩字符串:
BQAAAB+LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXW YWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyLmeVlW/w+GphA2BQAAAA==
然后我需要解压它。有人可以给我一个例子并完成以下方法吗?
private String compressString(String input) {
//...
}
private String decompressString(String input) {
//...
}
谢谢,
更新
根据后人的回答,现在我有以下4种方法。 Android 和.net 压缩和解压缩方法。除一种情况外,这些方法彼此兼容。我的意思是它们在前 3 个状态下兼容,但在第 4 个状态下不兼容:
- 状态 1) Android.compress <-> Android.解压缩:(确定)
- 状态 2) Net.compress <-> Net.decompress: (确定)
- 状态 3) Net.compress -> Android.解压缩:(确定)
- 状态 4) Android.compress -> .Net.解压缩:(不行)
有人能解决吗?
Android 方法:
public static String compress(String str) throws IOException {
byte[] blockcopy = ByteBuffer
.allocate(4)
.order(java.nio.ByteOrder.LITTLE_ENDIAN)
.putInt(str.length())
.array();
ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(str.getBytes());
gos.close();
os.close();
byte[] compressed = new byte[4 + os.toByteArray().length];
System.arraycopy(blockcopy, 0, compressed, 0, 4);
System.arraycopy(os.toByteArray(), 0, compressed, 4,
os.toByteArray().length);
return Base64.encode(compressed);
}
public static String decompress(String zipText) throws IOException {
byte[] compressed = Base64.decode(zipText);
if (compressed.length > 4)
{
GZIPInputStream gzipInputStream = new GZIPInputStream(
new ByteArrayInputStream(compressed, 4,
compressed.length - 4));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int value = 0; value != -1;) {
value = gzipInputStream.read();
if (value != -1) {
baos.write(value);
}
}
gzipInputStream.close();
baos.close();
String sReturn = new String(baos.toByteArray(), "UTF-8");
return sReturn;
}
else
{
return "";
}
}
.Net 方法:
public static string compress(string text)
{
byte[] buffer = Encoding.UTF8.GetBytes(text);
MemoryStream ms = new MemoryStream();
using (GZipStream zip = new GZipStream(ms, 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);
}
public static string decompress(string compressedText)
{
byte[] gzBuffer = Convert.FromBase64String(compressedText);
using (MemoryStream ms = new MemoryStream())
{
int msgLength = BitConverter.ToInt32(gzBuffer, 0);
ms.Write(gzBuffer, 4, gzBuffer.Length - 4);
byte[] buffer = new byte[msgLength];
ms.Position = 0;
using (GZipStream zip = new GZipStream(ms, CompressionMode.Decompress))
{
zip.Read(buffer, 0, buffer.Length);
}
return Encoding.UTF8.GetString(buffer);
}
}
I need an example for compressing a string using GZip in android. I want to send a string like "hello" to the method and get the following zipped string:
BQAAAB+LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyLmeVlW/w+GphA2BQAAAA==
Then I need to decompress it. Can anybody give me an example and complete the following methods?
private String compressString(String input) {
//...
}
private String decompressString(String input) {
//...
}
Thanks,
update
According to scessor's answer, Now I have the following 4 methods. Android and .net compress and decompress methods. These methods are compatible with each other except in one case. I mean they are compatible in the first 3 states but incompatible in the 4th state:
- state 1) Android.compress <-> Android.decompress: (OK)
- state 2) Net.compress <-> Net.decompress: (OK)
- state 3) Net.compress -> Android.decompress: (OK)
- state 4) Android.compress -> .Net.decompress: (NOT OK)
can anybody solve it?
Android methods:
public static String compress(String str) throws IOException {
byte[] blockcopy = ByteBuffer
.allocate(4)
.order(java.nio.ByteOrder.LITTLE_ENDIAN)
.putInt(str.length())
.array();
ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(str.getBytes());
gos.close();
os.close();
byte[] compressed = new byte[4 + os.toByteArray().length];
System.arraycopy(blockcopy, 0, compressed, 0, 4);
System.arraycopy(os.toByteArray(), 0, compressed, 4,
os.toByteArray().length);
return Base64.encode(compressed);
}
public static String decompress(String zipText) throws IOException {
byte[] compressed = Base64.decode(zipText);
if (compressed.length > 4)
{
GZIPInputStream gzipInputStream = new GZIPInputStream(
new ByteArrayInputStream(compressed, 4,
compressed.length - 4));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int value = 0; value != -1;) {
value = gzipInputStream.read();
if (value != -1) {
baos.write(value);
}
}
gzipInputStream.close();
baos.close();
String sReturn = new String(baos.toByteArray(), "UTF-8");
return sReturn;
}
else
{
return "";
}
}
.Net methods:
public static string compress(string text)
{
byte[] buffer = Encoding.UTF8.GetBytes(text);
MemoryStream ms = new MemoryStream();
using (GZipStream zip = new GZipStream(ms, 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);
}
public static string decompress(string compressedText)
{
byte[] gzBuffer = Convert.FromBase64String(compressedText);
using (MemoryStream ms = new MemoryStream())
{
int msgLength = BitConverter.ToInt32(gzBuffer, 0);
ms.Write(gzBuffer, 4, gzBuffer.Length - 4);
byte[] buffer = new byte[msgLength];
ms.Position = 0;
using (GZipStream zip = new GZipStream(ms, CompressionMode.Decompress))
{
zip.Read(buffer, 0, buffer.Length);
}
return Encoding.UTF8.GetString(buffer);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
GZIP 方法:
和一个测试:
=== 更新 ===
如果您需要 .Net 兼容性,我的代码必须稍作更改:
您可以使用相同的测试脚本。
The GZIP methods:
And a test:
=== Update ===
If you need .Net compability my code has to be changed a little:
You can use the same test script.
不管是什么,将“Hello”压缩为 BQAAAB+LC... 都是 gzipper 的一个特别糟糕的实现。它使用动态块而不是 deflate 格式的静态块,将“Hello”扩展得远远超出了必要的范围。删除 gzip 流的四字节前缀(始终以十六进制 1f 8b 开头)后,“Hello”扩展到 123 字节。在压缩领域,这被认为是犯罪。
您所抱怨的压缩方法工作正常。它生成一个静态块,总共输出 25 个字节。 gzip 格式具有 10 字节标头和 8 字节尾部开销,使 5 字节输入被编码为 7 字节。这更像是这样。
不可压缩的流将被扩展,但扩展幅度不应太大。 gzip 使用的 deflate 格式将为不可压缩数据每 16K 到 64K 添加 5 个字节。
为了获得实际的压缩,通常您需要给压缩器更多的时间来处理这五个字节,以便它可以在可压缩数据中找到重复的字符串和有偏差的统计信息。我知道您只是用短字符串进行测试。但在实际应用中,您永远不会使用具有如此短字符串的通用压缩器,因为只发送字符串总是更好。
Whatever it was that compressed "Hello" to BQAAAB+LC... is a particularly poor implementation of a gzipper. It expanded "Hello" far, far more than necessary, using a dynamic block instead of a static block in the deflate format. After removing the four-byte prefix to the gzip stream (which always starts with hex 1f 8b), "Hello" was expanded to 123 bytes. In the world of compression, that is considered a crime.
The Compress method that you are complaining about is working correctly and properly. It is generating a static block and a total output of 25 bytes. The gzip format has a ten-byte header and eight-byte trailer overhead, leaving the five-byte input having been coded in seven bytes. That's more like it.
Streams that are not compressible will be expanded, but it shouldn't be by much. The deflate format used by gzip will add five bytes to every 16K to 64K for incompressible data.
To get actual compression, in general you need to give the compressor much more to work with that five bytes, so that it can find repeated strings and biased statistics in compressible data. I understand that you were just doing tests with a short string. But in an actual application, you would never use a general-purpose compressor with such short strings, since it would always be better to just send the string.
在
Decompress()
方法中,Base64 解码输入的前 4 个字节在传递给GZipInputStream
之前会被跳过。在本例中,这些字节为05 00 00 00
。因此,在 Compress() 方法中,必须将这些字节放回到 Base64 编码之前。如果我这样做,压缩()返回以下内容:
我知道这与您的期望不完全相同,即:
但是,如果我的结果插回
Decompress()
,我想你仍然会得到“Hello”
。尝试一下。差异可能是由于获得原始字符串的压缩级别不同所致。那么神秘的前缀字节
05 00 00 00
是什么?根据这个答案,它可能是压缩字符串的长度,以便程序知道解压缩的字节缓冲区应该有多长。但在本例中这仍然不符合。这是 compress() 的修改代码:
更新:
Android 中的输出字符串与 .NET 代码不匹配的原因是 .NET GZip 实现执行更快的压缩(因此输出更大)。这可以通过查看原始 Base64 解码字节值来确定:
.NET:
我的 Android 版本:
现在,如果我们检查 GZip 文件格式,我们看到 .NET 和 Android 版本在初始阶段基本相同标头和尾部 CRC32 &大小字段。唯一的区别在于以下字段:
因此,从 XFL 字段可以清楚地看出 .NET 压缩算法产生更长的输出。
事实上,当我使用这些原始数据值创建一个二进制文件,然后使用gunzip解压缩它们时,.NET 和 Android 版本都给出完全相同的输出 “你好”。
因此,您不必担心不同的结果。
In your
Decompress()
method, the first 4 bytes of the Base64 decoded input are skipped before passing toGZipInputStream
. These bytes are found to be05 00 00 00
in this particular case. So in theCompress()
method, these bytes have to be put back in just before the Base64 encode.If I do this, Compress() returns the following:
I know that this is not exactly the same as your expectation, which is:
But, if my result is plugged back into
Decompress()
, I think you'll still get"Hello"
. Try it. The difference may be due to the different compression level with which you got the original string.So what are the mysterious prefixed bytes
05 00 00 00
? According to this answer it may be the length of the compressed string so that the program knows how long the decompressed byte buffer should be. Still that does not tally in this case.This is the modified code for compress():
Update:
The reason why the output strings in Android and your .NET code don't match is that the .NET GZip implementation does a faster compression (and thus larger output). This can be verified for sure by looking at the raw Base64 decoded byte values:
.NET:
My Android version:
Now if we check the GZip File Format, we see that both the .NET and Android versions are mostly identical in the initial header and trailing CRC32 & Size fields. The only differences are in the below fields:
So it's clear from the XFL field that the .NET compression algorithm produces longer output.
Infact, when I creates a binary file with these raw data values and then uncompressed them using gunzip, both the .NET and Android versions gave exactly the same output as "hello".
So you don't have to bother about the differing results.
我在我的项目中尝试了你的代码,发现Android上的压缩方法存在编码错误:
在上面的代码中,你应该使用正确的编码,并填充字节长度,而不是字符串长度:
I tried your code in my project, and found a encoding bug in compress method on Android:
on above code, u should use the corrected encoding, and fill the bytes length, not the string length:
好吧,我讨厌在有大量现有答案时插话,但不幸的是,由于各种原因,大多数答案都是错误的:
如果您使用 .NET Framework 4.5,这里是您需要的 C# 类(Base64 作为奖励):
这是您需要的 Android/Java 类:
所以,你就可以了 - 无依赖、100% 工作压缩的 Android/Java/C#/.NET 类。如果您发现不适用于 .NET 4.5 的字符串(我已经尝试过从“Hello world”到 1000 字短篇小说的所有内容),请告诉我。
OK, I hate chiming in when there are tons of existing answers, but unfortunately most of them are simply wrong for variety of reasons:
If you are using .NET Framework 4.5 here is C# class you need (Base64 as a bonus):
And here is Android/Java class you need:
So, there you go - dependency free, 100% working compression Android/Java/C#/.NET classes. If you find string that's not working with .NET 4.5 (I've tried everything from "Hello world" to 1000 word short story) - let me know.
我对这个问题感到抓狂。最后,在我的例子(.Net 4)中,没有必要在开头添加这个额外的 4 个字节来实现 .Net 兼容性。
它的工作原理如下:
Android 压缩:
.Net 解压缩
I got crazy with this issue. At the end, in my case (.Net 4) it was not necessary to add this extra 4 bytes at the begining for the .Net compatibility.
It works simply like this:
Android Compress:
.Net Decompress
Android方法解压不行
Android 压缩 -> OK:
.Net解压-> OK:
.Net压缩-> OK:
Android解压->不好:
Android method decompress not ok
Android Compress -> OK:
.Net Decompress -> OK:
.Net Compress -> OK:
Android Decompress -> Not OK:
这是一个帮助您入门的简单示例。
Here's a simple example to get you started.
这可能会迟到,但可能对某人有用,
我最近需要在 C# Xamarin 中压缩字符串并在 Android 中解压缩。基本上,Xamarin Android 应用程序会向另一个本机 Android 应用程序发送带有压缩的额外字符串的意图。 Android应用程序在使用它之前必须解压它。
这些是对我有用的方法。
ANDROID 解压
Xamarin 压缩
This might be late but could be useful for someone,
I recently had a requirement to compress a string in C# Xamarin and decompress it in Android. Basically a Xamarin android app sends another Native Android app an intent with a compressed extra string. And the Android app has to decompress it before using it.
These are the methods that worked for me.
ANDROID DECOMPRESS
XAMARIN COMPRESS
我在 Vb.net 中这样做:
I do it so in Vb.net: