[RESOLVIDO] Zipar uma String e armazenar em outra String

Olá Pessoal!

Preciso compactar uma String e armazenar em outra String e, se for necessário, preciso que meu código leia a String compactada e descompacte.

O código deste link http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/Deflater.html faz o que eu preciso, mas se eu resolvo separar a parte que compacta num método e a parte que descompacta em outro, o código passa a não descompactar mais, ou seja, retorna uma String vazia.

Meu código ficou assim:

[code]import java.util.zip.Deflater;
import java.util.zip.Inflater;

public class Zip {

private static int quantidade = 0;

public static void main(String[] args) {
    String s = compacta("8888887777yyyyeeeeeiiiiaoaoaooooaaaaaa");
    System.out.println(s);
    System.out.println(descompacta(s, quantidade));
}

public static String compacta(String expressao) {
    try {
        byte[] input = expressao.getBytes("UTF-8");
        byte[] output = new byte[100];
        Deflater compresser = new Deflater();
        compresser.setInput(input);
        compresser.finish();
        quantidade = compresser.deflate(output);
        return new String(output, 0, quantidade, "UTF-8");
    } catch (java.io.UnsupportedEncodingException ex) {
        ex.printStackTrace();
        return "";
    }
}

public static String descompacta(String expressao, int tam) {
    try {
        byte output[] = expressao.getBytes();
        Inflater decompresser = new Inflater();
        decompresser.setInput(output, 0, tam);
        byte[] result = new byte[100];
        int resultLength = decompresser.inflate(result);
        decompresser.end();
        return new String(result, 0, resultLength, "UTF-8");
    } catch (java.io.UnsupportedEncodingException ex) {
        ex.printStackTrace();
        return "";
    } catch (java.util.zip.DataFormatException ex) {
        ex.printStackTrace();
        return "";
    }
}

}[/code]

Existe tbem um tutorial neste site http://www.developer.com/java/other/article.php/10936_3603066_3 usando o algoritmo de Huffman, que tem o mesmo problema.
Se eu invoco o método de compactar e descompactar no mesmo método, é retornado o valor correto, mas se eu resolvo invocar em momentos diferentes, o programa não descompacta, retorna uma String vazia.

Eu preciso urgente que isso funcione.

Obrigada!

Não compacte dados e jogue em uma String; jogue em um byte[]. Isso é porque o construtor de String que recebe um byte[] SEMPRE estraga o byte[] ao tentar convertê-lo para uma String. Isso é “by design” e não tem jeito mesmo.

Se é realmente obrigatório usar uma String (por exemplo, para pôr em um campo VARCHAR em um banco de dados, ou para pôr em um campo HIDDEN em uma página), então você não pode ter dados binários dentro dela; portanto, é necessário codificar o byte[] para uma String usando Base-64.

A codificação Base-64 aumenta um pouco o tamanho dos dados (ou seja, cada 3 bytes são convertidos para 4 caracteres), mas ainda compensa.

Se você realmente não pode aumentar tanto o tamanho dos dados, você pode usar a codificação Base-85, que converte cada 4 bytes para 5 caracteres.

Ou seja:

Processo de compressão:
String -> byte[] -> compressão -> byte[] -> Base-64 -> String

Processo de descompressão:
String -> Base64 -> byte[] -> descompressão -> byte[] -> String

OK?

Olá, thingol!!

Obrigada pela dica, vou fazer isso…

mas o meu problema é que esse código não está descompactando… :frowning:
se eu pegar esse código todo e colocar tudo num método só, ele compacta e descompacta. Mas se eu deixar em métodos separados (preciso que seja assim), somente compacta e na hora de descompactar retorna uma String vazia…

A minha teoria é que para descompactar, ele precise de algum objeto que foi criado anteriomente.
Preciso muito resolver esse problema.

Obrigada!

O problema é que o retorno do primeiro método vem corrompido. Faça como o Thingol disse que funciona.

Está começando a funcionar…

Mas ainda tem um porém: Quando eu vou compactar usando o método compresser.deflate(output); , tenho que passar como parâmetro um array de bytes com um tamanho pré definido. O problema disso é que ele retorna um monte de AAAAAAAAAAAAA nos espaços que supostamente deveria ser espaços vazios do array, e a String compactada fica maior que a original (em decorrência desse monte de AAAAAAAAA).

eu pensei em retornar um substring, mas não deu certo.

Segue meu código de agora, que recebe a String

"22101091728000000004000000000000002AGUA OXIG.20 VOL 1000ML LBS                                                     LBS       41807FR       NNN000000000000100000000750000750A300000000002"

e retorna

eJwzMjI0MDSwNDQ3sjCAAhMDFGDk6B7qqOAf4emuZ2SgEObvo2AIFPX1UfBxClYgByD0mRhaGJi7 BUF5fn5+yPYawhjmplDS0RjJVQBLRiFPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAA=

[code]import java.io.IOException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

public class Zip {

public static void main(String[] args) {
    //testes
    String s = compacta("22101091728000000004000000000000002AGUA OXIG.20 VOL 1000ML LBS                          " +
            "                           LBS       41807FR       NNN000000000000100000000750000750A300000000002", 185);
    System.out.println(s);
    System.out.println(descompacta(s, 185));
}

public static String compacta(String expressao, int tamanho) {
    //String -> byte[] -> compressão -> byte[] -> Base-64 -> String
    byte[] input = expressao.getBytes();
    byte[] output = new byte[tamanho];
    Deflater compresser = new Deflater();
    compresser.setInput(input);
    compresser.finish();
    compresser.deflate(output);

    sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
    String s = enc.encode(output);
    return s;
}

public static String descompacta(String expressao, int tamanho) {
    //String -> Base64 -> byte[] -> descompressão -> byte[] -> String
    try {
        sun.misc.BASE64Decoder dec = new sun.misc.BASE64Decoder();
        byte output[] = dec.decodeBuffer(expressao);

        Inflater decompresser = new Inflater();
        decompresser.setInput(output);
        byte[] result = new byte[tamanho];
        decompresser.inflate(result);
        decompresser.end();
        return new String(result);
    } catch (java.util.zip.DataFormatException ex) {
        ex.printStackTrace();
        return "";
    } catch (IOException e) {
        e.printStackTrace();
        return "";
    }
}

}[/code]

Esse monte de AAAAA é o resultado da conversão dos zeros binários (que foram os bytes não usados pelo compactador ou descompactador) que o Base-64 transformou em AAAAA.
Dica: você não pegou o resultado de “compresser.deflate” ou “decompresser.inflate”. (Veja o javadoc, em http://java.sun.com/javase/6/docs/api/java/util/zip/Inflater.html e http://java.sun.com/javase/6/docs/api/java/util/zip/Deflater.html )
Esse resultado indica quantos bytes foram compactados ou descompactados. Então você passa esse valor da quantidade de bytes para o conversor de Base-64, para que ele não tente converter todos os bytes do buffer (que deve ser um pouco maior que os dados compactados!) e sim apenas a quantidade certa.

Obrigada thingol e Mark_Ameba pelas dicas e eis aqui o código funcionando!!
Encontrei aqui neste mesmo fórum uma dica do thingol para este site: http://commons.apache.org/codec/userguide.html que tem um código de comversão de um array de bytes para Base64.

Como eu não sabia como fazer esse o cálculo para descobrir em quantos caracteres resultariam a conversão, eu abri o código que está no site para download e vi como que o pessoal que programou fez pra calcular.

Obrigada mais uma vez!

[code]import java.util.zip.Deflater;
import java.util.zip.Inflater;
import org.apache.commons.codec.binary.Base64;

/**
*

  • @author Eliangela
    */
    public class Zip {

    public static void main(String[] args) {
    String s = compacta("22101091728000000004000000000000002AGUA OXIG.20 VOL 1000ML LBS LBS 41807FR NNN000000000000100000000750000750A300000000002", 185);
    System.out.println(s);
    System.out.println(descompacta(s, 185));
    }

    public static String compacta(String expressao, int tamanho) {
    //String -> byte[] -> compressão -> byte[] -> Base-64 -> String
    Deflater compresser = new Deflater();
    byte[] input = expressao.getBytes();
    byte[] output = new byte[tamanho];

     compresser.setInput(input);
     compresser.finish();
    
     int quant = compresser.deflate(output);
     int tamanhoArray = 0;
    
     if ((quant * 8) % 24 != 0) {
         tamanhoArray = (((quant * 8) / 24) + 1) * 4;
     } else {
         tamanhoArray = ((quant * 8) / 24) * 4;
     }
    
     return new String(Base64.encodeBase64(output)).substring(0, tamanhoArray);
    

    }

    public static String descompacta(String expressao, int tamanho) {
    //String -> Base64 -> byte[] -> descompressão -> byte[] -> String
    try {
    byte output[] = Base64.decodeBase64(expressao.getBytes());

         Inflater decompresser = new Inflater();
         decompresser.setInput(output);
         byte[] result = new byte[tamanho];
         decompresser.inflate(result);
         decompresser.end();
         return new String(result);
     } catch (java.util.zip.DataFormatException ex) {
         ex.printStackTrace();
         return "";
     }
    

    }
    }[/code]

E ae amigo estou com um problema neste seu algoritmo, ao testa-lo a string retornada depois da descompactação é esta "22101091728000000004000000000000002AGUA OXIG.20 VOL 1000ML LBS "
ou seja ela está faltando conteudo, vc tem alguma ideia do pq isso estar ocorrendo?
Obrigado!

Olha, realmente eu tive alguns problemas depois… mas consegui resolver…

[code]
public static String descompacta(String expressao, int tamanho) {
//String -> Base64 -> byte[] -> descompressão -> byte[] -> String
int tamanhoDescompactado = 0;
try {
byte output[] = Base64.decodeBase64(expressao.getBytes());
StringBuffer sb = new StringBuffer();

        Inflater decompresser = new Inflater();
        decompresser.setInput(output);
        byte[] result = new byte[tamanho];
        while (!decompresser.finished()) {
            tamanhoDescompactado += decompresser.inflate(result);
            sb.append(new String(result));
            result = new byte[tamanho];
        }
        decompresser.end();
        return sb.toString().substring(0, tamanhoDescompactado);
    } catch (java.util.zip.DataFormatException ex) {
        ex.printStackTrace();
        return "";
    }
}[/code]

Consegui resolver foi o tamanho do array e byte que estava errado na hora de descompactar.

então… foi por isso que eu tive que colocar while (!decompresser.finished()), porque daí eu iria ter que ficar informando certinho o tamanho do array.

cara aqui deu certo com este while mas se for uma mensagem grande, se eu colocar uma mensagem “teste teste” da erro, tu sabe uma forma de resolver isso?

Cara consegui resolver desta forma, assim independete do tamanho da mensagem ele descompacta.

public static String descompactar(byte[] mensagem) {
        byte[] novo = new byte[100];
        StringBuffer aux = new StringBuffer();
        try {
            Inflater dec = new Inflater();
            dec.setInput(mensagem);
            dec.inflate(novo);
            aux.append(new String(novo));
            int tam = dec.getTotalIn();
            int pos = dec.getTotalOut();
            while (pos < tam) {
                pos += dec.inflate(novo);
                aux.append(new String(novo));
                novo = new byte[100];
            }
            dec.end();
        } catch (DataFormatException ex) {
            ex.printStackTrace();
        }
        return aux.toString().trim();
    }

obrigada pela sua colaboração, Shakall!!!
acho que eu não tinha percebido o erro porque eu estava usando textos muito grandes pra compactar!

Affff!!!
Tinha um erro na minha compactação, Shakall, por isso que as Strings pequenas estavam dando erro!
Agora está aceitando vários tamanhos. Testei com tamanho 1, 20, e aprox. 2000 caracteres.
Acredito que agora não está mais dando erros!

[code]import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import org.apache.commons.codec.binary.Base64; //download da API em: http://commons.apache.org/downloads/download_codec.cgi
import org.apache.commons.lang.ArrayUtils; //download da API em: http://commons.apache.org/downloads/download_lang.cgi

/**
*

  • @author Eliangela
    */
    public class Zip {

    private static final int TAMANHO_ARRAY = 1024;

    public static String compacta(String expressao) {
    int tamanhoArray = 0;
    int quant = 0;
    int total = 0;

     Deflater compresser = new Deflater();
    
     byte[] aSerCompactado = expressao.getBytes();
     byte[] output = null;
     byte[] temp = null;
    
     compresser.setInput(aSerCompactado);
     compresser.finish();
     while (true) {
         temp = new byte[TAMANHO_ARRAY];
         quant = compresser.deflate(temp);
         if (quant == 0) {
             break;
         }
         total += quant;
         output = ArrayUtils.addAll(output, temp);
     }
     compresser.end();
    
     if ((total * 8) % 24 != 0) {
         tamanhoArray = (((total * 8) / 24) + 1) * 4;
     } else {
         tamanhoArray = ((total * 8) / 24) * 4;
     }
    
     return new String(Base64.encodeBase64(output)).substring(0, tamanhoArray);
    

    }

    public static String descompacta(String expressao) {
    byte[] result = null;
    int faltante = -1;
    StringBuffer sb = new StringBuffer();
    byte[] aSerDescompactado = Base64.decodeBase64(expressao.getBytes());

     String s;
     try {
         Inflater decompresser = new Inflater();
         decompresser.setInput(aSerDescompactado);
    
         while (true) {
             result = new byte[TAMANHO_ARRAY];
             faltante = decompresser.inflate(result);
             if (faltante == 0) {
                 break;
             }
    
             s = new String(result);
             sb.append(s);
         }
         decompresser.end();
     } catch (DataFormatException ex) {
         ex.printStackTrace();
     }
     return sb.toString().trim();
    

    }

    public static void main(String[] args) {
    String msg = “100000000000000000000000000000000222222222222222222222222222555555555588888888888888888888888888444444444441”;
    String comp = compacta(msg, 0);
    System.out.println(comp);
    String desc = descompacta(comp, 0);
    System.out.println(desc);
    }
    }[/code]
    :wink:

Valeu cara!