android - मैं GZIPOutputStream का उपयोग कर स्ट्रिंग को ज़िप और अनजिप कैसे कर सकता हूं जो नेट के साथ संगत है?




.net compression (6)

आपकी Decompress() विधि में, GZipInputStream इनपुट के पहले 4 बाइट GZipInputStream जाने से पहले छोड़ दिए GZipInputStream । इस विशेष मामले में ये बाइट 05 00 00 00 पाए जाते हैं। तो Compress() विधि में, इन बाइट्स को बेस 64 एन्कोड से ठीक पहले वापस रखना होगा।

अगर मैं ऐसा करता हूं, तो संपीड़न () निम्नलिखित देता है:

BQAAAB+LCAAAAAAAAADLSM3JyQcAhqYQNgUAAAA=

मुझे पता है कि यह आपकी उम्मीद के समान नहीं है, जो है:

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

लेकिन, अगर मेरा परिणाम वापस Decompress() में प्लग किया गया है, तो मुझे लगता है कि आपको अभी भी "Hello" मिल जाएगा। कोशिश करो। अंतर अलग संपीड़न स्तर के कारण हो सकता है जिसके साथ आपको मूल स्ट्रिंग मिलती है।

तो रहस्यमय prefixed बाइट्स 05 00 00 00 क्या हैं? इस उत्तर के अनुसार यह संपीड़ित स्ट्रिंग की लंबाई हो सकती है ताकि प्रोग्राम जानता है कि डिकंप्रेस्ड बाइट बफर कितना समय होना चाहिए। फिर भी इस मामले में तालमेल नहीं है।

यह संपीड़न () के लिए संशोधित कोड है:

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

अद्यतन करें:

एंड्रॉइड और आपके .NET कोड में आउटपुट स्ट्रिंग का मिलान नहीं होने का कारण यह है कि .NET GZip कार्यान्वयन एक तेज़ संपीड़न (और इस प्रकार बड़ा आउटपुट) करता है। यह कच्चे बेस 64 डीकोडेड बाइट मानों को देखकर निश्चित रूप से सत्यापित किया जा सकता है:

नेट:

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

मेरा एंड्रॉइड संस्करण:

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

अब यदि हम जीजेआईपी फ़ाइल प्रारूप की जांच करते हैं, तो हम देखते हैं कि .NET और Android दोनों संस्करण प्रारंभिक शीर्षलेख और सीआरसी 32 और आकार फ़ील्ड के पीछे समान हैं। नीचे के क्षेत्रों में केवल अंतर ही हैं:

  • .NET के मामले में XFL = 04 (कंप्रेसर सबसे तेज़ एल्गोरिदम का उपयोग किया जाता है), जबकि यह एंड्रॉइड में 00 है
  • वास्तविक संपीड़ित ब्लॉक

तो यह एक्सएफएल फ़ील्ड से स्पष्ट है कि .NET संपीड़न एल्गोरिदम लंबे आउटपुट का उत्पादन करता है।

असल में, जब मैं इन कच्चे डेटा मानों के साथ एक द्विआधारी फ़ाइल बनाता हूं और फिर उन्हें गनज़िप का उपयोग करके असम्पीडित करता है, तो .NET और Android संस्करण दोनों ने "हेलो" के समान आउटपुट दिया।

तो आपको अलग-अलग परिणामों के बारे में परेशान करने की ज़रूरत नहीं है।

मुझे एंड्रॉइड में GZip का उपयोग करके एक स्ट्रिंग को संपीड़ित करने के लिए एक उदाहरण की आवश्यकता है। मैं विधि में "हैलो" जैसी स्ट्रिंग भेजना चाहता हूं और निम्न ज़िप्ड स्ट्रिंग प्राप्त करना चाहता हूं:

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

तो मुझे इसे डिकंप्रेस करने की ज़रूरत है। क्या कोई मुझे एक उदाहरण दे सकता है और निम्नलिखित विधियों को पूरा कर सकता है?

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

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

धन्यवाद,

अद्यतन करें

कैसर के जवाब के अनुसार, अब मेरे पास निम्नलिखित 4 विधियां हैं। एंड्रॉइड और .NET संपीड़न और decompress विधियों। ये विधियां एक मामले को छोड़कर एक दूसरे के साथ संगत हैं। मेरा मतलब है कि वे पहले 3 राज्यों में संगत हैं लेकिन चौथे राज्य में असंगत हैं:

  • राज्य 1) ​​Android.compress <-> Android.decompress: ( ठीक है )
  • राज्य 2) Net.compress <-> Net.decompress: ( ठीक है )
  • राज्य 3) Net.compress -> Android.decompress: ( ठीक है )
  • राज्य 4) Android.compress -> .Net.decompress: ( ठीक नहीं है )

क्या कोई इसे हल कर सकता है?

एंड्रॉइड विधियां:

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

नेट तरीकों:

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

जीजेआईपी विधियों:

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 compability की आवश्यकता है तो मेरे कोड को थोड़ा बदलना होगा:

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

आप एक ही टेस्ट स्क्रिप्ट का उपयोग कर सकते हैं।


बीक्यूएएएबी + एलसी को संकुचित "हैलो" जो कुछ भी था, वह एक गीजर का विशेष रूप से खराब कार्यान्वयन है। यह डिफ्लेट प्रारूप में स्थिर ब्लॉक के बजाय एक गतिशील ब्लॉक का उपयोग करके, "हैलो" को ज़्यादा से ज़्यादा ज़रूरी है। Gzip स्ट्रीम (जो हमेशा हेक्स 1 एफ 8 बी के साथ शुरू होता है) के लिए चार बाइट उपसर्ग को हटाने के बाद, "हैलो" को 123 बाइट्स तक बढ़ाया गया था। संपीड़न की दुनिया में, इसे अपराध माना जाता है।

जिस कंप्रेस विधि के बारे में आप शिकायत कर रहे हैं वह ठीक से और सही तरीके से काम कर रहा है। यह एक स्थिर ब्लॉक और 25 बाइट्स का कुल उत्पादन उत्पन्न कर रहा है। जीजीआईपी प्रारूप में दस बाइट हेडर और आठ-बाइट ट्रेलर ओवरहेड है, जिससे पांच बाइट इनपुट को सात बाइट्स में कोड किया गया है। यह हुई ना बात।

धाराएं जो संपीड़ित नहीं हैं, का विस्तार किया जाएगा, लेकिन यह बहुत अधिक नहीं होना चाहिए। Gzip द्वारा उपयोग किए गए डिफ्लेट प्रारूप में असम्पीडित डेटा के लिए प्रत्येक 16K से 64K तक पांच बाइट जोड़े जाएंगे।

वास्तविक संपीड़न प्राप्त करने के लिए, सामान्य रूप से आपको कंप्रेसर को उस पांच बाइट्स के साथ काम करने के लिए बहुत कुछ देना होगा, ताकि यह संपीड़ित डेटा में बार-बार तार और पक्षपातपूर्ण आंकड़े पा सकें। मैं समझता हूं कि आप केवल एक छोटी स्ट्रिंग के साथ परीक्षण कर रहे थे। लेकिन एक वास्तविक अनुप्रयोग में, आप कभी भी इस तरह के छोटे तारों के साथ एक सामान्य उद्देश्य कंप्रेसर का उपयोग नहीं करेंगे, क्योंकि स्ट्रिंग को भेजने के लिए हमेशा बेहतर होगा।


मैं इस मुद्दे के साथ पागल हो गया। अंत में, मेरे मामले में (.Net 4) .Net संगतता की शुरुआत में इस अतिरिक्त 4 बाइट्स को जोड़ना आवश्यक नहीं था।

यह बस इस तरह काम करता है:

एंड्रॉइड संपीड़न:

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

मैंने अपने प्रोजेक्ट में अपना कोड आज़माया, और एंड्रॉइड पर संपीड़न विधि में एक एन्कोडिंग बग पाया:

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

शुरू करने के लिए यहां एक सरल उदाहरण दिया गया है।

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




gzip