【问题标题】:How can I Zip and Unzip a string using GZIPOutputStream that is compatible with .Net?如何使用与 .Net 兼容的 GZIPOutputStream 压缩和解压缩字符串?
【发布时间】:2011-10-06 17:25:49
【问题描述】:

我需要一个在 android 中使用 GZip 压缩字符串的示例。我想向方法发送一个像“hello”这样的字符串并得到以下压缩字符串:

BQAAAB+LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyLmeVWl

那我需要解压。谁能给我一个例子并完成以下方法?

private String compressString(String input) {
    //...
}

private String decompressString(String input) {
    //...
}

谢谢,


更新

根据scessor's answer,现在我有以下4种方法。 Android 和 .net 压缩和解压缩方法。除一种情况外,这些方法彼此兼容。我的意思是它们在前 3 个状态是兼容的,但在第 4 个状态是不兼容的:

  • 状态 1) Android.compress Android.decompress: (OK)
  • 状态 2) Net.compress Net.decompress: (OK)
  • 状态 3) Net.compress -> Android.decompress: (OK)
  • 状态 4) Android.compress -> .Net.decompress: (不行)

有人能解决吗?

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);
    }
}

【问题讨论】:

  • compressString 的结果不能是String,它必须是bytes[]。同样,decompressString 的输入不能是 String,也必须是 bytes[]
  • 反之亦然”是什么意思?使用String 压缩GZIPOutputStream? ;-)
  • 我的 .Net 方法创建的大字符串与 Android 中 compress 方法的结果不同。我更新了我的问题并插入了 .Net Compress 和 Decompress 方法。任何人都可以更改这些方法来创建相同的压缩字符串吗?
  • 有许多可能的、有效的“压缩”字符串将解压缩为字符串“Hello”。如果两个不同的压缩器产生不同的输出,那没关系,只要它们在所有输出上都正确解压缩。对于 NOT OK 情况,您从解压器得到什么输出?
  • 当我将 android 压缩结果提供给 .Net 解压缩方法时,出现异常

标签: android .net compression zip gzip


【解决方案1】:

好的,当有大量现有答案时,我讨厌插话,但不幸的是,由于各种原因,大多数答案都是错误的:

  • .NET Framework 中的 GZIP 算法之间存在差异。如果您使用的是 .NET 4.5,那么您在不同答案中看到的大多数投诉根本不适用于您(而不是那些使用 2.0 或 3.5 的人)。如果你使用“固定”版本的代码,你实际上会搞砸压缩/解压缩。
  • Java 使用无符号字节[],.NET 使用有符号字节[]。这可能会在传输过程中导致问题,具体取决于您传输该字节 [] 的准确程度。
  • 我使用 Base64 来传输字节[],这可能会带来更多问题。还有很多其他原因,但让我们跳过进一步的抱怨,直接进入代码......

如果您使用的是 .NET Framework 4.5,这里是您需要的 C# 类(Base64 作为奖励):

public class CompressString
{
    private static void CopyTo(Stream src, Stream dest)
    {
        byte[] bytes = new byte[4096];
        int cnt;

        while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0)
        {
            dest.Write(bytes, 0, cnt);
        }
    }

    public static byte[] Zip(string str)
    {
        var bytes = Encoding.UTF8.GetBytes(str);

        using (var msi = new MemoryStream(bytes))
        using (var mso = new MemoryStream())
        {
            using (var gs = new GZipStream(mso, CompressionMode.Compress))
            {
                //msi.CopyTo(gs);
                CopyTo(msi, gs);
            }

            return mso.ToArray();
        }
    }

    public static string Unzip(byte[] bytes)
    {
        using (var msi = new MemoryStream(bytes))
        using (var mso = new MemoryStream())
        {
            using (var gs = new GZipStream(msi, CompressionMode.Decompress))
            {
                //gs.CopyTo(mso);
                CopyTo(gs, mso);
            }

            return Encoding.UTF8.GetString(mso.ToArray());
        }
    }

    // Base64
    public static string ZipBase64(string compress)
    {
        var bytes = Zip(compress);
        var encoded = Convert.ToBase64String(bytes, Base64FormattingOptions.None);
        return encoded;
    }

    public static string UnzipBase64(string compressRequest)
    {
        var bytes = Convert.FromBase64String(compressRequest);
        var unziped = Unzip(bytes);
        return unziped;
    }

    // Testing
    public static bool TestZip(String stringToTest)
    {
        byte[] compressed = Zip(stringToTest);
        Debug.WriteLine("Compressed to " + compressed.Length + " bytes");
        String decompressed = Unzip(compressed);
        Debug.WriteLine("Decompressed to: " + decompressed);

        return stringToTest == decompressed;
    }
}

这是您需要的 Android/Java 类:

public class CompressString {
    public static byte[] compress(String string) {
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
            GZIPOutputStream gos = new GZIPOutputStream(os);
            gos.write(string.getBytes());
            gos.close();
            byte[] compressed = os.toByteArray();
            os.close();
            return compressed;
        } catch (IOException ex) {
            return null;
        }
    }

    public static String decompress(byte[] compressed) {
        try {
            final int BUFFER_SIZE = 32;
            ByteArrayInputStream is = new ByteArrayInputStream(compressed);
            GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
            byte[] data = new byte[BUFFER_SIZE];
            int bytesRead;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while ((bytesRead = gis.read(data)) != -1) {
                baos.write(data, 0, bytesRead);
            }
            gis.close();
            is.close();
            return baos.toString("UTF-8");
        } catch (IOException ex) {
            return null;
        }
    }    

    // Base64
    public static String compressBase64(String strToCompress) {
        byte[] compressed = compress(strToCompress);
        String encoded = android.util.Base64.encodeToString(compressed, android.util.Base64.NO_WRAP);
        return encoded;
    }

    public static String decompressBase64(String strEncoded) {
        byte[] decoded = android.util.Base64.decode(strEncoded, android.util.Base64.NO_WRAP);
        String decompressed = decompress(decoded);
        return decompressed;
    }


    // test
    public static boolean testCompression(String stringToTest) {
        byte[] compressed = compress(stringToTest);
        Log.d("compress-test", "Compressed to " + compressed.length + " bytes");
        String decompressed = decompress(compressed);
        Log.d("compress-test", "Decompressed to " + decompressed);

        return stringToTest.equals(decompressed);
    }
}

所以,你去 - 无依赖,100% 工作压缩 Android/Java/C#/.NET 类。如果您发现 不适用于 .NET 4.5 的字符串(我已经尝试过从“Hello world”到 1000 字短篇小说的所有内容) - 请告诉我。

【讨论】:

  • 看起来很有希望,但我用 c# api 压缩的 20k JSON 文件尝试了这个,Android 说它是bad-base 64。
  • @ClumsyHamster 上传您的文件并在此处留下链接...我一直在多个项目中使用此代码,它对我来说 100% 有效。
  • 没关系。我才发现我做错了什么。我返回一个对象的对象,只有对象被压缩,但我试图做没有被压缩的对象本身。很抱歉浪费了您的时间。
【解决方案2】:

这可能会迟到,但可能对某人有用, 我最近需要在 C# Xamarin 中压缩字符串并在 Android 中解压缩。基本上,一个 Xamarin android 应用程序向另一个 Native Android 应用程序发送一个带有压缩额外字符串的意图。并且安卓应用需要解压才能使用。

这些是对我有用的方法。

安卓解压

 public static String decompress(String zipText) throws IOException {
    int size = 0;
    byte[] gzipBuff = Base64.decode(zipText,Base64.DEFAULT);
    ByteArrayInputStream memstream = new ByteArrayInputStream(gzipBuff, 4,gzipBuff.length - 4);
    GZIPInputStream gzin = new GZIPInputStream(memstream);
    final int buffSize = 8192; 
   byte[] tempBuffer = new byte[buffSize];
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
  while ((size = gzin.read(tempBuffer, 0, buffSize)) != -1) { 
   baos.write(tempBuffer, 0, size);
  } byte[] buffer = baos.toByteArray();
  baos.close(); return new String(buffer, StandardCharsets.UTF_8);
  }

XAMARI 压缩

public static string CompressString(string text)
    {
        byte[] buffer = Encoding.UTF8.GetBytes(text);
        var memoryStream = new MemoryStream();
        using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
        {
            gZipStream.Write(buffer, 0, buffer.Length);
        }
        memoryStream.Position = 0;
        var compressedData = new byte[memoryStream.Length];
        memoryStream.Read(compressedData, 0, compressedData.Length);
        var gZipBuffer = new byte[compressedData.Length + 4];
        Buffer.BlockCopy(compressedData, 0, gZipBuffer, 4, compressedData.Length);
        Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gZipBuffer, 0, 4);
        return Convert.ToBase64String(gZipBuffer);
    }

【讨论】:

    【解决方案3】:

    我在 Vb.net 中这样做:

      Public Function zipString(ByVal Text As String) As String
        Dim res As String = ""
        Try
    
            Dim buffer As Byte() = System.Text.Encoding.UTF8.GetBytes(Text)
            Dim ms As New MemoryStream()
            Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress, True)
                zipStream.Write(buffer, 0, buffer.Length)
            End Using
            ms.Position = 0
            Dim outStream As New MemoryStream()
            Dim compressed As Byte() = New Byte(ms.Length - 1) {}
            ms.Read(compressed, 0, compressed.Length)
            Dim gzBuffer As Byte() = New Byte(compressed.Length + 3) {}
            System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length)
            System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4)
            res = Convert.ToBase64String(gzBuffer)
        Catch ex As Exception
            Log("mdl.zipString: " & ex.Message)
        End Try
        Return res
    End Function
    
    Public Function unzipString(ByVal compressedText As String) As String
        Dim res As String = ""
        Try
            Dim gzBuffer As Byte() = Convert.FromBase64String(compressedText)
            Using ms As New MemoryStream()
                Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)
                ms.Write(gzBuffer, 4, gzBuffer.Length - 4)
                Dim buffer As Byte() = New Byte(msgLength - 1) {}
                ms.Position = 0
                Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress)
                    zipStream.Read(buffer, 0, buffer.Length)
                End Using
                res = System.Text.Encoding.UTF8.GetString(buffer, 0, buffer.Length)
            End Using
        Catch ex As Exception
            Log("mdl.unzipString: " & ex.Message)
        End Try
        Return res
    End Function
    

    【讨论】:

    • 这不能回答问题,因为代码不适用于 Android。我建议删除它。
    • @DoronYakovlev-Golani 尽管语言发生了变化,这个答案的核心逻辑是否有价值?如果是这样,我会使用否决票?元数据说我们用这个做什么?
    • @Shawn 我认为这个答案试图回答“如何在 VB 中压缩/解压缩”字符串?”。这并没有回答所问的问题(该问题仅适用于 Android),并且作为我不希望看到与此问题相关的 SO 用户。
    【解决方案4】:

    这里有一个简单的例子让你开始。

    public static void main(String[] args) throws IOException 
    {
        byte[] buffer = new byte[4096];
        StringBuilder sb = new StringBuilder();
    
        //read file to compress
    
        String read = readFile( "spanish.xml", Charset.defaultCharset());
    
        if( read != null )
        {
            //compress file to output
    
            FileOutputStream fos = new FileOutputStream("spanish-new.xml");
            GZIPOutputStream gzos = new GZIPOutputStream(fos);
            gzos.write( read.getBytes());
            gzos.close();
    
            //uncompress and read back
    
            FileInputStream fis = new FileInputStream("spanish-new.xml");
            GZIPInputStream gzis = new GZIPInputStream(fis);
    
            int bytes = 0;
    
            while ((bytes = gzis.read(buffer)) != -1) {
                sb.append( new String( buffer ) );
            }
        }
    }
    
    static String readFile(String path, Charset encoding) throws IOException {
        byte[] encoded = Files.readAllBytes(Paths.get(path));
        return new String(encoded, encoding);
    }
    

    【讨论】:

      【解决方案5】:

      Android方法解压不行

      Android 压缩 -> 好的:

      public static byte[] compress(String string) throws IOException {
          ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
          GZIPOutputStream gos = new GZIPOutputStream(os);
          gos.write(string.getBytes());
          gos.close();
          byte[] compressed = os.toByteArray();
          os.close();
          return compressed;
      }
      

      .Net 解压 -> 确定:

      public static byte[] DecompressViD(byte[] gzip)
      {
          // Create a GZIP stream with decompression mode.
          // ... Then create a buffer and write into while reading from the GZIP stream.
          using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
          {
              const int size = 4096;
              byte[] buffer = new byte[size];
              using (MemoryStream memory = new MemoryStream())
              {
                  int count = 0;
                  do
                  {
                      count = stream.Read(buffer, 0, size);
                      if (count > 0)
                      {
                          memory.Write(buffer, 0, count);
                      }
                  }
                  while (count > 0);
                  return memory.ToArray();
              }
          }
      }
      

      .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);
      
              return Convert.ToBase64String(compressed);
          }
      

      Android 解压 -> 不行:

      public static String decompress(String zipText) throws IOException {
          byte[] compressed = Base64.decode(zipText);
      
          GZIPInputStream os = new GZIPInputStream(new ByteArrayInputStream(compressed));
      
          GZIPInputStream gzipInputStream = new GZIPInputStream(os);
      
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          for (int value = 0; value != -1;) {
              value = gzipInputStream.read();
              if (value != -1) {
                  baos.write(value);
              }
          }
          gzipInputStream.close();
          baos.close();
      
          return new String(baos.toByteArray(), "UTF-8");
      }
      

      【讨论】:

        【解决方案6】:

        我对这个问题发疯了。最后,在我的情况下(.Net 4),没有必要在开始时添加额外的 4 个字节以实现 .Net 兼容性。

        它的工作原理是这样的:

        Android 压缩:

        public static byte[] compress(String string) throws IOException {
            ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
            GZIPOutputStream gos = new GZIPOutputStream(os);
            gos.write(string.getBytes());
            gos.close();
            byte[] compressed = os.toByteArray();
            os.close();
            return compressed;
        }
        

        .Net 解压

        public static byte[] DecompressViD(byte[] gzip)
            {
                // Create a GZIP stream with decompression mode.
                // ... Then create a buffer and write into while reading from the GZIP stream.
                using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
                {
                    const int size = 4096;
                    byte[] buffer = new byte[size];
                    using (MemoryStream memory = new MemoryStream())
                    {
                        int count = 0;
                        do
                        {
                            count = stream.Read(buffer, 0, size);
                            if (count > 0)
                            {
                                memory.Write(buffer, 0, count);
                            }
                        }
                        while (count > 0);
                        return memory.ToArray();
                    }
                }
            }
        

        【讨论】:

          【解决方案7】:

          我在我的项目中尝试了你的代码,发现 Android 上 compress 方法的编码错误:

          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());
          

          在上面的代码中,你应该使用正确的编码,并填写字节长度,而不是字符串长度:

          byte[] data = str.getBytes("UTF-8");
          
          byte[] blockcopy = ByteBuffer
                  .allocate(4)
                  .order(java.nio.ByteOrder.LITTLE_ENDIAN)
                  .putInt(data.length)
                      .array();
          
          ByteArrayOutputStream os = new ByteArrayOutputStream( data.length );    
          GZIPOutputStream gos = new GZIPOutputStream(os);
          gos.write( data );
          

          【讨论】:

            【解决方案8】:

            在您的 Decompress() 方法中,Base64 解码输入的前 4 个字节在传递给 GZipInputStream 之前被跳过。在这种特殊情况下,这些字节被发现是05 00 00 00。所以在 Compress() 方法中,这些字节必须在 Base64 编码之前放回。

            如果我这样做,Compress() 会返回以下内容:

            BQAAAB+LCAAAAAAAAADLSM3JyQcAhqYQNgUAAAA=
            

            我知道这和你的预期不完全一样,即:

            BQAAAB+LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyLmeVlW/w+GphA2BQAAAA==
            

            但是,如果我的结果重新插入Decompress(),我想你仍然会得到"Hello"。试试吧。差异可能是由于您获得原始字符串的压缩级别不同。

            那么神秘的前缀字节05 00 00 00是什么?根据this answer,它可能是压缩字符串的长度,以便程序知道解压缩的字节缓冲区应该多长。在这种情况下仍然不符合。

            这是 compress() 修改后的代码:

            public static String Compress(String text) throws IOException {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
            
                // TODO: Should be computed instead of being hard-coded
                baos.write(new byte[]{0x05, 0, 0, 0}, 0, 4);
            
                GZIPOutputStream gzos = new GZIPOutputStream(baos);
                gzos.write(text.getBytes());
                gzos.close();
            
                return Base64.encode(baos.toByteArray());
            }
            

            更新:

            Android 中的输出字符串与您的 .NET 代码不匹配的原因是 .NET GZip 实现的压缩速度更快(因此输出更大)。这可以通过查看原始 Base64 解码字节值来确定:

            .NET:

            1F8B 0800 0000 0000 0400 EDBD 0760 1C49 9625 262F 6DCA 7B7F 4AF5 4AD7 E074 A108 8060 1324 D890 4010 ECC1 88CD E692 EC1D 6947 2329 AB2A 81CA 6556 655D 6616 40CC ED9D BCF7 DE7B EFBD F7DE 7BEF BDF7 BA3B 9D4E 27F7 DFFF 3F5C 6664 016C F6CE 4ADA C99E 2180 AAC8 1F3F 7E7C 1F3F 22E6 7959 56FF 0F86 A610 3605 0000 00

            我的 Android 版本:

            1F8B 0800 0000 0000 0000 CB48 CDC9 C907 0086 A610 3605 0000 00

            现在,如果我们检查 GZip File Format,我们会发现 .NET 和 Android 版本的初始标头和尾随 CRC32 和大小字段基本相同。唯一的区别在于以下字段:

            • 在 .NET 中为 XFL = 04(压缩器使用最快的算法),而在 Android 中为 00
            • 实际压缩块

            因此,从 XFL 字段中可以清楚地看出 .NET 压缩算法会产生更长的输出。

            事实上,当我使用这些原始数据值创建一个二进制文件,然后使用 gunzip 解压缩它们时,.NET 和 Android 版本都给出了与“hello”完全相同的输出

            因此您不必担心不同的结果。

            【讨论】:

            • 生成更多输出并不快。它更慢。在做出静态与动态决策之前,查找匹配字符串需要相同的时间。几乎不需要时间来预测静态与动态的输出大小,然后在这种情况下(静态)做出正确的决定。然后,在这种特殊情况下,为静态生成更短的输出需要更少的时间。这里的交易不是更快的压缩器,而是更短的开发时间(或懒惰的开发人员)而不是编写完整的 deflator。
            • @MarkAdler 你可能是对的,但我想说的是这两种方法给出的结果不同,因为它们的压缩方案不同。 OP 担心结果为何不同。
            【解决方案9】:

            GZIP 方法:

            public static byte[] compress(String string) throws IOException {
                ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
                GZIPOutputStream gos = new GZIPOutputStream(os);
                gos.write(string.getBytes());
                gos.close();
                byte[] compressed = os.toByteArray();
                os.close();
                return compressed;
            }
            
            public static String decompress(byte[] compressed) throws IOException {
                final int BUFFER_SIZE = 32;
                ByteArrayInputStream is = new ByteArrayInputStream(compressed);
                GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
                StringBuilder string = new StringBuilder();
                byte[] data = new byte[BUFFER_SIZE];
                int bytesRead;
                while ((bytesRead = gis.read(data)) != -1) {
                    string.append(new String(data, 0, bytesRead));
                }
                gis.close();
                is.close();
                return string.toString();
            }
            

            还有一个测试:

            final String text = "hello";
            try {
                byte[] compressed = compress(text);
                for (byte character : compressed) {
                    Log.d("test", String.valueOf(character));
                }
                String decompressed = decompress(compressed);
                Log.d("test", decompressed);
            } catch (IOException e) {
                e.printStackTrace();
            }
            

            === 更新 ===

            如果您需要 .Net 兼容性,我的代码必须稍作更改:

            public static byte[] compress(String string) throws IOException {
                byte[] blockcopy = ByteBuffer
                    .allocate(4)
                    .order(java.nio.ByteOrder.LITTLE_ENDIAN)
                    .putInt(string.length())
                    .array();
                ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
                GZIPOutputStream gos = new GZIPOutputStream(os);
                gos.write(string.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 compressed;
            }
            
            public static String decompress(byte[] compressed) throws IOException {
                final int BUFFER_SIZE = 32;
                ByteArrayInputStream is = new ByteArrayInputStream(compressed, 4, compressed.length - 4);
                GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
                StringBuilder string = new StringBuilder();
                byte[] data = new byte[BUFFER_SIZE];
                int bytesRead;
                while ((bytesRead = gis.read(data)) != -1) {
                    string.append(new String(data, 0, bytesRead));
                }
                gis.close();
                is.close();
                return string.toString();
            }
            

            您可以使用相同的测试脚本。

            【讨论】:

            • 伙计,我为你脱帽致敬!这个网站上很少有人知道单元测试以及为什么要这样做!当之无愧的+1!
            • 感谢您的回答。我测试您的解决方案,但我没有得到的字符串:“BQAAAB + LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee ++ 997o7nU4n99 // P1xmZAFs9s5K2smeIYCqyB8 / fnwfPyLmeVlW / W + GphA2BQAAAA ==”你的方法的结果是不一样的。提到的字符串是由 .Net 中的 gzip 算法为单词“hello”生成的。我需要生成这个字符串。我该怎么做?
            • 我找到了解压方法,它工作正常。但是 Compress 方法不能正常工作。
            • public static String Decompress(String zipText) throws IOException { int size = 0; byte[] gzipBuff = Base64.decode(zipText); ByteArrayInputStream memstream = new ByteArrayInputStream(gzipBuff, 4,gzipBuff.length - 4); GZIPInputStream gzin = new GZIPInputStream(memstream); final int buffSize = 8192; byte[] tempBuffer = new byte[buffSize]; ByteArrayOutputStream baos = new ByteArrayOutputStream(); while ((size = gzin.read(tempBuffer, 0, buffSize)) != -1) { baos.write(tempBuffer, 0, size); } byte[] buffer = baos.toByteArray(); baos.close(); return new String(buffer, "UTF-8"); }
            • public static String Compress(String text) throws IOException { byte[] gzipBuff = EncodingUtils.getBytes(text, "UTF-8"); ByteArrayOutputStream bs = new ByteArrayOutputStream(); GZIPOutputStream gzin = new GZIPOutputStream(bs); gzin.write(gzipBuff); gzin.finish(); bs.close(); byte[] buffer = bs.toByteArray(); gzin.close(); return Base64.encode(buffer); }
            【解决方案10】:

            无论将“Hello”压缩成 BQAAAB+LC 是什么...都是 gzipper 的一个特别糟糕的实现。它使用动态块而不是 deflate 格式的静态块来扩展“你好”,远远超过必要的程度。在删除 gzip 流的四字节前缀(始终以十六进制 1f 8b 开头)后,“Hello”扩展为 123 字节。在压缩世界中,这被视为犯罪。

            您抱怨的 Compress 方法工作正常且正常。它正在生成一个静态块和 25 个字节的总输出。 gzip 格式有一个 10 字节的头部和 8 字节的尾部开销,剩下的 5 字节输入被编码为 7 个字节。这更像是。

            不可压缩的流将被扩展,但应该不会太大。 gzip 使用的 deflate 格式将在每 16K 到 64K 中添加五个字节用于不可压缩的数据。

            为了得到实际的压缩,通常你需要给压缩器更多的处理这五个字节,以便它可以在可压缩数据中找到重复的字符串和有偏差的统计信息。我了解您只是在使用短字符串进行测试。但在实际应用中,您永远不会使用具有如此短字符串的通用压缩器,因为只发送字符串总是更好。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-11-12
              • 1970-01-01
              • 1970-01-01
              • 2013-05-12
              • 1970-01-01
              相关资源
              最近更新 更多