it-swarm.com.de

Wie kann ich einen String mit GZIPOutputStream komprimieren und entpacken, der mit .Net kompatibel ist?

Ich brauche ein Beispiel für die Komprimierung einer Zeichenfolge mit GZip in Android. Ich möchte eine Zeichenfolge wie "Hallo" an die Methode senden und die folgende gezippte Zeichenfolge erhalten:

AnkNachbebenanstAbebenAnknebenansechsteinAnhebenAnsehenderAnhebenAnsehenderAnhebenAnsehenderAngebnebenAnhebenAnpassenAnsehenderAngebebenAnkunebenAnhebenAnhebenAnhebenAnhebenAnhebenAnhebenderAnhebenAnTebenAnThebenAnbuchenAnTebenAnThebenAnbuchenAnbuchenAnbuchenAnbuchenAnbuchenAnbuchenAnbuchenAnbuchenAnhebenderAn Speichern zu optimieren abbuchenAnbuchenAnbuchenAn AbbrechenAnbuchen AbbrechenAnbuchenAnbuchenObbuchenderierenAnbuchen aktivierenAnbuchenAnbuchenAnbuchenAnbuchen übernehmenAnbuchen abbuchen aktivieren, an dem AnzeigenAnnAnstellen geht abbuchenAnbuchenAnbuchenInnOnInn annAnnOnInnzunehmen vorbei zu übernehmen, an der zu übernehmen an derAn derbrAn aus zu aktivieren an derAn derAn derbrAnse hinzuzufügen zu suchen, bei der an dieser Person zu erfolgen

Dann muss ich es dekomprimieren. Kann mir jemand ein Beispiel geben und die folgenden Methoden ausführen?

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

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

Vielen Dank,


update

Laut Antwort von scessor , habe ich nun die folgenden 4 Methoden. Android und .net Komprimierungs- und Dekompressionsmethoden. Diese Methoden sind bis auf einen Fall miteinander kompatibel. Ich meine, sie sind in den ersten drei Zuständen kompatibel, im vierten jedoch nicht kompatibel:

  • zustand 1) Android.compress <-> Android.dekomprimieren: (OK)
  • zustand 2) Net.compress <-> Net.decompress: (OK)
  • zustand 3) Net.compress -> Android.decompress: (OK)
  • zustand 4) Android.compress -> .Net.decompress: (NOT OK)

kann jemand es lösen?

Android-Methoden:

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-Methoden:

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

Die GZIP-Methoden:

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

Und ein Test:

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

=== Update ===

Wenn Sie die .Net-Kompatibilität benötigen, muss mein Code ein wenig geändert werden:

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

Sie können das gleiche Testskript verwenden.

83
scessor

Was auch immer das komprimierte "Hallo" zu BQAAAB + LC war ... ist eine besonders schlechte Implementierung eines Zwickers. Es hat "Hello" weitaus mehr als nötig erweitert, indem ein dynamischer Block anstelle eines statischen Blocks im Format deflate verwendet wurde. Nach dem Entfernen des 4-Byte-Präfixes zum gzip-Stream (der immer mit Hex 1f 8b beginnt) wurde "Hello" auf 123 Bytes erweitert. In der Welt der Kompression gilt das als Verbrechen.

Die Compress-Methode, über die Sie sich beschweren, funktioniert ordnungsgemäß und ordnungsgemäß. Es generiert einen statischen Block und eine Gesamtausgabe von 25 Bytes. Das gzip-Format hat einen 10-Byte-Header und einen acht-Byte-Trailer-Overhead, sodass die fünf-Byte-Eingabe in sieben Bytes codiert wurde. Das ist eher so.

Streams, die nicht komprimierbar sind, werden erweitert, sollten aber nicht viel sein. Das von gzip verwendete Deflate-Format fügt bei inkompressiblen Daten alle 16 KB bis 64 KB fünf Bytes hinzu.

Um eine tatsächliche Komprimierung zu erhalten, müssen Sie dem Kompressor im Allgemeinen viel mehr geben, um mit diesen fünf Bytes arbeiten zu können, damit er wiederholte Zeichenfolgen und verzerrte Statistiken in komprimierbaren Daten finden kann. Ich verstehe, dass Sie gerade Tests mit einer kurzen Saite durchgeführt haben. In einer tatsächlichen Anwendung würden Sie jedoch niemals einen Universal-Kompressor mit so kurzen Strings verwenden, da es immer besser wäre, den String einfach zu senden.

14
Mark Adler

Ich habe Ihren Code in meinem Projekt ausprobiert und einen Codierungsfehler in der Kompressionsmethode unter Android gefunden:

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

im obigen Code sollte u die korrigierte Kodierung verwenden und die Byteslänge und nicht die Zeichenfolgenlänge füllen:

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 );
4
Halowb

In Ihrer Decompress()-Methode werden die ersten 4 Bytes der Base64-decodierten Eingabe übersprungen, bevor sie an GZipInputStream übergeben werden. Es wird festgestellt, dass diese Bytes in diesem speziellen Fall 05 00 00 00 sind. Bei der Compress()-Methode müssen diese Bytes kurz vor der Base64-Codierung wieder eingefügt werden.

Wenn ich das mache, gibt Compress () folgendes zurück:

BQAAAB+LCAAAAAAAAADLSM3JyQcAhqYQNgUAAAA=

Ich weiß, dass dies nicht genau Ihre Erwartung ist, nämlich:

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

Aber wenn mein Ergebnis wieder mit Decompress() verbunden ist, werden Sie trotzdem "Hello" bekommen. Versuch es. Der Unterschied kann in der unterschiedlichen Komprimierungsstufe liegen, mit der Sie die Originalzeichenfolge erhalten haben.

Was sind also die mysteriösen vorangestellten Bytes 05 00 00 00? Entsprechend dieser Antwort kann es sich um die Länge des komprimierten Strings handeln, damit das Programm weiß, wie lang der dekomprimierte Bytepuffer sein soll. Aber das stimmt in diesem Fall nicht überein.

Dies ist der modifizierte Code für 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());
}

Aktualisieren:

Der Grund dafür, dass die Ausgabestrings in Android und Ihr .NET-Code nicht übereinstimmen, besteht darin, dass die .NET GZip-Implementierung eine schnellere Komprimierung durchführt (und somit eine größere Ausgabe). Dies kann sicher überprüft werden, indem die rohen Base64-decodierten Byte-Werte betrachtet werden:

.NETZ:

1F8B 0800 0000 0000 0400 EDBD 0760 1C49 
 9625 262F 6DCA 7B7F 4AF5 4AD7 E074 A108 .__8060 1324 D890 4010 ECC1 88CD E692 EC1D 
 9D4E 27F7 DFFF 3F5C 6664 016C F6CE 4ADA 
 C99E 2180 AAC8 1F3F 7E7C 1F3F 22E6 7959 
 56FF 0F86 A610 3605 0000 00 

Meine Android-Version:

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

Wenn wir nun das GZip File Format überprüfen, sehen wir, dass sowohl die .NET- als auch die Android-Version in den Feldern für den ursprünglichen Header und die nachfolgenden CRC32- und Size-Felder weitgehend identisch sind. Die einzigen Unterschiede bestehen in den folgenden Feldern:

  • XFL = 04 (am schnellsten eingesetzter Kompressor) bei .NET, bei Android ist es 00
  • Die tatsächlichen komprimierten Blöcke

Aus dem XFL-Feld ist also klar, dass der .NET-Kompressionsalgorithmus längere Ausgaben erzeugt.

Tatsächlich, wenn ich eine Binärdatei mit diesen Rohdatenwerten erstellt und sie dann mit gunzip dekomprimiert habe, gaben sowohl die .NET- als auch die Android-Version genau die gleiche Ausgabe wie "Hallo".

Sie müssen sich also nicht um die unterschiedlichen Ergebnisse kümmern.

4
Dheeraj V.S.

Ich wurde verrückt mit dieser Ausgabe. Am Ende war es in meinem Fall (.Net 4) nicht erforderlich, diese zusätzlichen 4 Bytes zu Beginn für die .Net-Kompatibilität hinzuzufügen. 

Es funktioniert einfach so:

Android Compress:

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 dekomprimieren

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();
            }
        }
    }
2
Ivan BASART

OK, ich hasse es, zu klingen, wenn es Tonnen von Antworten gibt, aber leider sind die meisten aus verschiedenen Gründen falsch:

  • Es gibt Unterschiede zwischen GZIP-Algorithmen in .NET Framework. Wenn Sie .NET 4.5 verwenden, gelten die meisten Beschwerden, die Sie in verschiedenen Antworten sehen, nicht für Sie (sondern für diejenigen, die 2.0 oder 3.5 verwenden). Wenn Sie mit "festen" Versionen von Code arbeiten, werden Sie die Komprimierung/Dekomprimierung tatsächlich durcheinanderbringen.
  • Java verwendet das nicht signierte Byte [], .NET verwendet das signierte Byte []. Dies kann zu Problemen während des Transports führen, abhängig davon, wie genau Sie dieses Byte [] transportieren.
  • Ich habe Base64 zum Transportieren von Byte [] verwendet, was noch mehr Probleme verursachen kann. Es gibt verschiedene andere Gründe, aber lassen Sie uns weiter jammern und kommen Sie zum Code ...

Wenn Sie .NET Framework 4.5 verwenden, benötigen Sie eine C # -Klasse (Base64 als Bonus):

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

Und hier ist die Android/Java-Klasse, die Sie benötigen:

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);
            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();
        } 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 == decompressed;
    }
}

Also, los gehts - abhängige, 100% funktionierende Komprimierungsklasse für Android/Java/C # /. NET. Wenn Sie eine Zeichenfolge finden, die funktioniert nicht mit .NET 4.5 (ich habe alles von "Hallo Welt" bis zu 1000 Word-Kurzgeschichte versucht), lassen Sie es mich wissen.

1
kape123

Android-Methode dekomprimieren nicht ok

Android Compress -> OK:

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 Dekomprimieren -> OK:

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 Compress -> OK:

    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 Dekomprimieren -> Nicht OK:  

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

Hier ist ein einfaches Beispiel für den Einstieg.

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