Streams: Arquivos

Computação II - Ciência da Computação


Prof.: Danilo S. Carvalho

Nessa aula, aprenderemos sobre como lidar com arquivos, e os fluxos envolvidos na transferência de dados de e para arquivos.


Ao final da aula, poderemos ler e gravar arquivos a partir dos nossos programas.

Na grande maioria dos casos práticos de programação, os dados que estamos interessados em processar não vêm da entrada padrão, mas estão em arquivos armazenados tipicamente na memória secundária do computador.


Arquivos são agrupamentos lógicos de dados que podemos acessar através de um endereço único à mídia onde estão armazenados. São geralmente organizados na forma de um sistema de arquivos.

Os sistemas de arquivos tipicamente usados são organizados hierarquicamente em diretórios e arquivos. Onde os diretórios podem conter arquivos e outros diretórios.

Para acessar os dados de um arquivo em Java há duas estruturas distintas: o ponteiro de arquivo e o fluxo de entrada/saída para arquivo.

O ponteiro para um arquivo é representado pela classe File (java.io).

Um objeto do tipo File armazena os atributos de um arquivo: nome, localização no sistema de arquivos (path), tamanho, permissões de leitura/escrita, entre outros.

Também pode ser usado para criar ou deletar um arquivo.

                        
                            import java.io.*;

                            public class ExemploArquivo {

                                public static void main(String[] args) {
                                    File arq = new File("/etc/protocols");
                                    
                                    System.out.println(arq.getAbsolutePath());
                                    System.out.println(arq.length() / 1024);
                                }

                            }
                        
                    
                        
                            import java.io.*;

                            public class ExemploArquivo {

                                public static void main(String[] args) {
                                    File arq = new File("ABC.java");
                                    try {
                                        arq.createNewFile();
                                    }
                                    catch (IOException e) {
                                        System.err.println("Erro ao criar arquivo.");
                                        return;
                                    }
                                    
                                    System.out.println(arq.getAbsolutePath());
                                    System.out.println(arq.length());
                                    
                                    arq.delete();
                                }

                            }
                        
                    

Os fluxos de entrada/saída para arquivo são representados pelas classes FileInputStream e FileOutputStream (java.io).

Podemos instanciar um objeto do tipo FileInputStream usando um objeto File ou uma string contendo a localização do arquivo desejado.

Com um objeto FileInputStream, podemos ler os bytes do arquivo com o método read (e suas sobrecargas).

Mas isso não é muito útil, pois sabemos o formato do arquivo e queremos interpretar os dados da forma mais apropriada.

Por exemplo, sabemos que o arquivo /etc/protocols contem texto, então queremos interpretar o seu conteúdo como uma string.

Para isso, precisamos de uma classe que vai interpretar os dados que passam pelo FileInputStream e codificá-los apropriadamente para nós.

No caso de texto, podemos usar o Scanner (java.util) para ler strings e os tipos primitivos a partir de um arquivo de texto.

Há ainda uma outra maneira de ler rapidamente todo o conteúdo de um arquivo de texto para uma sequência de strings, usando a classe Files (java.nio.file), como no exemplo abaixo:

Para ler arquivos binários, usamos a classe DataInputStream, que interpreta os dados do arquivo para os tipos primitivos correspondentes.

                        
                            import java.io.*;

                            public class ExemploArquivoEntrada {

                                public static void main(String[] args) {
                                    File arq = new File("/etc/protocols");
                                    
                                    try (FileInputStream fis = new FileInputStream(arq)) {
                                        byte[] bytesArquivo = new byte[10];
                                        fis.read(bytesArquivo);
                                        
                                        for (byte b : bytesArquivo) {
                                            System.out.println(b);
                                        }
                                    }
                                    catch (FileNotFoundException e) {
                                        System.err.println("Arquivo não encontrado: " + arq.getAbsolutePath());
                                    }
                                    catch (IOException e) {
                                        System.err.println("Problema ao abrir ou fechar o arquivo: " + arq.getAbsolutePath());
                                    }
                                    
                                }
                            }
                        
                    
                        
                            import java.io.*;
                            import java.util.*;

                            public class ExemploArquivoEntradaTexto {

                                public static void main(String[] args) {
                                    File arq = new File("/etc/protocols");
                                    
                                    try (FileInputStream fis = new FileInputStream(arq); 
                                        Scanner scanner = new Scanner(fis)) {
                                        while (scanner.hasNextLine()) {
                                            System.out.println(scanner.nextLine());
                                        }	
                                    }
                                    catch (FileNotFoundException e) {
                                        System.err.println("Arquivo não encontrado: " + arq.getAbsolutePath());
                                    }
                                    catch (IOException e) {
                                        System.err.println("Problema ao abrir ou fechar o arquivo: " + arq.getAbsolutePath());
                                    }
                                }
                            }
                        
                    
                        
                            import java.io.*;
                            import java.util.*;
                            import java.nio.file.*;

                            public class ExemploArquivoEntradaTexto {

                                public static void main(String[] args) {
                                    File arq = new File("/etc/protocols");
                                    
                                    try {
                                        List<String> linhas = Files.readAllLines(arq.toPath());
                                        
                                        for (String linha : linhas) {
                                            System.out.println(linha);
                                        }
                                    }
                                    catch (IOException e) {
                                        System.err.println("Problema ao abrir ou fechar o arquivo: " + arq.getAbsolutePath());
                                    }
                                }
                            }
                        
                    
                        
                            import java.io.*;
                            import java.util.*;

                            public class ExemploArquivoEntradaBinaria {
                                public static void main(String[] args) {
                                    File arq = new File("/tmp/ABC.bin");
                                    HashSet<Double> numeros = new HashSet<>();
                                    
                                    try (DataInputStream dis = new DataInputStream(new FileInputStream(arq))) {
                                        while (true) {
                                            numeros.add(dis.readDouble());
                                        }	
                                    }
                                    catch (EOFException e) {
                                        System.err.println(numeros.toString());
                                    }
                                    catch (IOException e) {
                                        System.err.println("Problema ao abrir ou fechar o arquivo: " + arq.getAbsolutePath());
                                    }
                                }
                            }
                        
                    

Da mesma forma, podemos instanciar um objeto do tipo FileOutputStream usando um objeto File ou uma string contendo a localização do arquivo desejado.

Opcionalmente, podemos passar um argumento boolean, informando se queremos gravar a partir do fim do arquivo (true) ou do início (false). O padrão é gravar no início (sobrescrever) o arquivo.

Para escrever texto formatado, podemos usar a classe PrintStream, passando em seu construtor um FileOutputStream ou File, e escrever a saída conforme aprendemos na aula de entrada/saída padrão.

Para escrever outros tipos de dados, podemos usar a classe DataOutputStream, passando em seu construtor um FileOutputStream.

Os métodos dessa classe escrevem a representação binária para o fluxo de saída subjacente, nesse caso o FileOutputStream, que por sua vez escreve no arquivo.

                        
                            import java.io.*;

                            public class ExemploArquivoSaidaTexto {

                                public static void main(String[] args) {
                                    File arq = new File("/tmp/ABC.txt");
                                    double[] numeros = {3.0, 2.5, 10, 44};
                                    
                                    
                                    try (FileOutputStream fos = new FileOutputStream(arq)) {
                                        PrintStream out = new PrintStream(fos);
                                        for (double num : numeros) {
                                            out.println(num);
                                        }
                                    }
                                    catch (IOException e) {
                                        System.out.println("Problema ao abrir ou fechar o arquivo: " + arq.getAbsolutePath());
                                    }
                                }

                            }
                        
                    
                        
                            import java.io.*;

                            public class ExemploArquivoSaidaBinaria {

                                public static void main(String[] args) {
                                    File arq = new File("/tmp/ABC.bin");
                                    double[] numeros = {3.0, 2.5, 10, 44};
                                    
                                    
                                    try (FileOutputStream fos = new FileOutputStream(arq)) {
                                        DataOutputStream out = new DataOutputStream(fos);
                                        for (double num : numeros) {
                                            out.writeDouble(num);
                                        }
                                    }
                                    catch (IOException e) {
                                        System.err.println("Problema ao abrir ou fechar o arquivo: " + arq.getAbsolutePath());
                                    }
                                }
                            }
                        
                    

Agora sabemos como ler e escrever arquivos em Java.


Acessamos strings e tipos primitivos. O próximo passo será ler e escrever objetos de tipos complexos.

Perguntas:

  1. Existe ainda uma terceira maneira de ler arquivos de texto usando classes do pacote java.io. Você consegue descobrir qual é?
  2. Como sabemos qual o tipo do arquivo que estamos lendo?

Exercício:

  1. Implemente uma classe Java que copie o conteúdo de um arquivo para outro, tendo como parâmetros as localizações dos arquivos origem e destino, e o modo (texto ou binário) como a cópia será feita.

Até a próxima aula!