GZIPInputStream lendo linha por linha

85

Tenho um arquivo no formato .gz. A classe java para ler este arquivo é GZIPInputStream. No entanto, essa classe não estende a classe BufferedReader de java. Como resultado, não consigo ler o arquivo linha por linha. Eu preciso de algo assim

reader  = new MyGZInputStream( some constructor of GZInputStream) 
reader.readLine()...

Pensei em criar minha classe que estende a classe Reader ou BufferedReader de java e use GZIPInputStream como uma de suas variáveis.

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
import java.util.zip.GZIPInputStream;

public class MyGZFilReader extends Reader {

    private GZIPInputStream gzipInputStream = null;
    char[] buf = new char[1024];

    @Override
    public void close() throws IOException {
        gzipInputStream.close();
    }

    public MyGZFilReader(String filename)
               throws FileNotFoundException, IOException {
        gzipInputStream = new GZIPInputStream(new FileInputStream(filename));
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        // TODO Auto-generated method stub
        return gzipInputStream.read((byte[])buf, off, len);
    }

}

Mas, isso não funciona quando eu uso

BufferedReader in = new BufferedReader(
    new MyGZFilReader("F:/gawiki-20090614-stub-meta-history.xml.gz"));
System.out.println(in.readLine());

Alguém pode aconselhar como proceder ..

Kapil D
fonte
veja este link stackoverflow.com/q/6717165/779408 . Um método de compactação e descompactação é representado lá.
Bobs de
1
Pelo amor de tudo o que é bom e correto neste mundo e pela sanidade de qualquer desenvolvedor que escreve código que valha a pena, mesmo remotamente ... ESTEJA CIENTE DE CODIFICAR COMO @erickson APONTE PARA FORA! Ele é a única resposta que aponta isso, o que me dá vontade de chorar.
James

Respostas:

143

A configuração básica dos decoradores é assim:

InputStream fileStream = new FileInputStream(filename);
InputStream gzipStream = new GZIPInputStream(fileStream);
Reader decoder = new InputStreamReader(gzipStream, encoding);
BufferedReader buffered = new BufferedReader(decoder);

A questão chave neste trecho é o valor de encoding. Esta é a codificação de caracteres do texto no arquivo. É "US-ASCII", "UTF-8", "SHIFT-JIS", "ISO-8859-9",…? existem centenas de possibilidades e a escolha correta geralmente não pode ser determinada a partir do próprio arquivo. Deve ser especificado por meio de algum canal fora da banda.

Por exemplo, talvez seja o padrão da plataforma. Em um ambiente de rede, no entanto, isso é extremamente frágil. A máquina que escreveu o arquivo pode estar no cubículo vizinho, mas tem uma codificação de arquivo padrão diferente.

A maioria dos protocolos de rede usa um cabeçalho ou outros metadados para observar explicitamente a codificação de caracteres.

Nesse caso, parece, pela extensão do arquivo, que o conteúdo é XML. O XML inclui o atributo "encoding" na declaração XML para esse propósito. Além disso, o XML deve realmente ser processado com um analisador XML, não como texto. Ler XML linha por linha parece um caso especial frágil.

Deixar de especificar explicitamente a codificação vai contra o segundo mandamento. Use a codificação padrão por sua conta e risco!

Erickson
fonte
1
obrigado, funcionou ... No entanto, não há necessidade de passo do leitor .. também podemos escrever como GZIPInputStream gzip = new GZIPInputStream (new FileInputStream ("F: /gawiki-20090614-stub-meta-history.xml.gz" )); BufferedReader br = novo BufferedReader (novo InputStreamReader (gzip));
Kapil D
12
@KapilD me deixa triste que você tenha perdido completamente o que ele quis dizer sobre a codificação ... como mostrado por seu comentário e o exemplo em seu comentário. Releia a resposta de Erickson ... talvez 30 vezes.
James
Como o comando gzip conhece a codificação? Eu quero ler muitos arquivos de vários servidores Linux / Unix de todo o mundo ... então eu quero ter certeza de que faço isso direito ... O post menciona que a codificação geralmente não pode ser determinada pelo próprio arquivo ... mas o comando gzip -d parece funcionar em qualquer arquivo sem entrada separada ... (é o que eu uso agora, mas quero contornar) então eu acho que se posso apenas descobrir o que o gzip faz para saber a codificação, pode fazer o mesmo. Quaisquer pensamentos / sugestões alguém pode me apontar na direção certa?
glyphx
@glyphx Sua pergunta não está clara. Você quer dizer como você pode reconhecer um arquivo gzip na ausência de alguma afirmação externa sobre o tipo de conteúdo? Uma dica é a extensão do arquivo, outra é a presença do número mágico 0x1F8B no cabeçalho do arquivo. No entanto, você não pode saber se um arquivo é um arquivo gzip válido até que realmente processe a coisa toda.
Erickson
1
Para deixar claro, sei que esses arquivos são arquivos gzip. E os arquivos gzipados são todos arquivos baseados em texto, como arquivos csv e pipe delim. Só quero ler esses arquivos diretamente com o java linha por linha. Posso gzip -d e depois lê-los linha por linha sem problemas. Fiquei confuso em seus comentários sobre ter que especificar a codificação ... Eu acho que a maioria dos arquivos são ASCII ... mas alguns podem ter caracteres asiáticos, então talvez UTF-8? Só quero ter certeza de que faço isso corretamente ... Ficou mais claro? Obrigado!
glyphx de
44
GZIPInputStream gzip = new GZIPInputStream(new FileInputStream("F:/gawiki-20090614-stub-meta-history.xml.gz"));
BufferedReader br = new BufferedReader(new InputStreamReader(gzip));
br.readLine();

ChssPly76
fonte
Sua resposta é ótima. Curto e conciso. No entanto, a resposta de Erickson é mais detalhada.
Kapil D
3
BufferedReader in = new BufferedReader(new InputStreamReader(
        new GZIPInputStream(new FileInputStream("F:/gawiki-20090614-stub-meta-history.xml.gz"))));

String content;

while ((content = in.readLine()) != null)

   System.out.println(content);
Arumugam Mathiazhagan
fonte
2

Você pode usar o seguinte método em uma classe de utilitários e sempre que necessário ...

public static List<String> readLinesFromGZ(String filePath) {
    List<String> lines = new ArrayList<>();
    File file = new File(filePath);

    try (GZIPInputStream gzip = new GZIPInputStream(new FileInputStream(file));
            BufferedReader br = new BufferedReader(new InputStreamReader(gzip));) {
        String line = null;
        while ((line = br.readLine()) != null) {
            lines.add(line);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace(System.err);
    } catch (IOException e) {
        e.printStackTrace(System.err);
    }
    return lines;
}
Memin
fonte
1

aqui está com uma linha

try (BufferedReader br = new BufferedReader(
        new InputStreamReader(
           new GZIPInputStream(
              new FileInputStream(
                 "F:/gawiki-20090614-stub-meta-history.xml.gz"))))) 
     {br.readLine();}
Domador
fonte