Tutorial Java - Arquivos

Introdução

    Os meios de armazenamento persistentes são fatores importantes na visa de um computador, já que a memória eletrônica é apagada quando ele é desligado. Caso não existissem, teriamos de resscrever dados de configuração, nomes, textos, entre outras coisas.

    O uso de mecanismo de leitura e escrita eleva a outro nível o poder de uma aplicação. Em uma maneira mais simples, permite que guardemos dados para uso posterior, que mantenhamos configurações de um dado programa ou que possamos ler um arquivo de terceiros ou gerar arquivos com os dados trabalhados até o momento.

    O foco deste tutorial será apresentar formas de ler e gravar dados em arquivos utilizando pacote java.io. Nesse pacote, encontramos classes que permitem interagir com o sistema de arquivos e realizar tarefas de leitura e escrita.

     



    A classe File é a classe do Java que mermite ter acesso às informações sobre o sistema de arquivos: nome, diretório, tamanho, permissões de escrita e leitura e outroas coisas mais.

    Como exemplo, um arquivo em disco de nome MeuTexto.txt, como mostra a seguir:

    Este é um arquivo texto simples.
    Segunda linha.
    Terceira linha.
    Fim.

    Para obeter informaçõe sobre este arquivo, observe o código da classe UsoDoFile.java  a seguir:

 import java.io.File;
import javax.swing.JOptionPane;

public class UsoDoFile {
public static void main(String[] args) {
String var = "";
File arquivo = new File("MeuTexto.txt");

var = "Nome: " + arquivo.getName() + "\n\r";

var += "Caminho: " + arquivo.getAbsolutePath() + "\n\r";
var += "Existe? " + arquivo.exists() + "\n\r";
var += "É um diretório? " + arquivo.isDirectory() + "\n\r";
var += "É um arquivo? " + arquivo.isFile() + "\n\r";
var += "Pode ser lido? " + arquivo.canRead() + "\n\r";
var += "Pode ser escrito? " + arquivo.canWrite() + "\n\r";

JOptionPane.showMessageDialog( null, var, "Informacao do Sistema", JOptionPane.INFORMATION_MESSAGE );

}
}

    Para o funcionamento deste programa, há necessidade que os arquivos MeuTexto.txt e UsoDoFile.java estejam no mesmo diretório .

    A classe File é ainda uma representação para arquivos e diretórios de sistema, trazendo informações adicionais sobre o sistema operacional, tais como qual caractere é separador de diretório, informações sobre discos disponíveis, etc.

    Em Java, a classe File permite representar arquivos nesse nível de abstração. Um dos construtores desta classe recebe como argumento uma string que pode identificar, por exemplo, o nome de um arquivo em disco. Os métodos desta classe permitem obter informações sobre o arquivo -- por exemplo, exists(), canRead(), canWrite(), length() e lastModified() -- e realizar operações sobre o arquivo como um todo, como em delete() e deleteOnExit().

    Para compreender as funcionalidade, observe o código da classe "InformacaoSistema.java" a seguir:

 import java.io.File;
import javax.swing.JOptionPane;

public class InformacaoSistema {
public static void main(String[] args) {
String var = "";
var = "Separador de Path: " + File.pathSeparator + "\n\r";
var += "Separador de Diretório: " + File.separator + "\n\r";

File discos [] = File.listRoots();
for (int cont=0; cont<discos.length; cont++) {
var += "Disco " + cont + ": " + discos[cont] + "\n\r";
}
JOptionPane.showMessageDialog( null, var, "Informacao do Sistema", JOptionPane.INFORMATION_MESSAGE );

}
}

    O programa obtem informações sobre o sistema operacional, o separador de variável de sistema path(caminho de atalho) e o separador para diretórios.

    Também é obtido através do programa, uma lista de discos existente localmente atravésdo método listRoots(), no caso do sistema Windows, ele retorna o nome (letras) dos drives lógicos existente. A seguir observe o resultado da execução do programa:

    No computador que esta sendo executado com sistema operacional Windows, onde o separador de path é o ponto e virgula(;) e o separador de diretório é a barra invertida (\). Ale disso, o computador de execução possuia um drive de disquete (A:\).

    A classe File representa o sistema de arquivos e seus objetos são as referências a arquivos e diretórios, existentes ou não. A referência aos arquivos é bastante útil para obtermos informações relevantes como:

    De forma genérica, havendo um dispositivo de entrada de dados habilitado, o programa obtém dados deste dispositivo através de uma operação read(). Similarmente, um dado pode ser enviado para um dispositivo de saída habilitado através de uma operação write().  

Leitura escrita

    Na linguagem Java, arquivos são considerados fonte, quando realizamos a leitura a partir dele; e destino, quando realizamos a escrita nele. Ao fazermos uma leitura, os dados partem da fonte e trafegam ordenados unidade a unidade até o programa ou classe Java. Da mesma forma, quando fazemos uma gravação, o programa envia os dados ordenado, unidade a unidade, para um destino.

    Fontes de dados manipuladas como seqüências de bytes são tratadas em Java pela classe InputStream e suas classes derivadas.

    2. InputStream 


    As principais subclasses de InputStream, já oferecidas como parte do pacote java.io, são:

    java.io.ByteArrayInputStream
        Valores são originários de um arranjo de bytes; 

    java.io.FileInputStream
        Bytes são originários de um arquivo; usualmente, usado em conjunto com BufferedInputStream e DataInputStream; 

    java.io.FilterInputStream
        Oferece as definições necessárias para filtrar dados de um InputStream. Útil através de alguma de suas classes derivadas:

    java.io.BufferedInputStream
        Lê transparentemente grandes volumes de bytes, armazenando-os em um buffer interno, melhorando a eficiência de operação para fontes de dados lentas; 

    java.io.DataInputStream
        Permite a leitura de representações binárias dos tipos primitivos de Java, oferecendo métodos tais como readBoolean(), readChar(), readDouble e readInt(). É uma implementação da interface DataInput. 

    java.io.PushbackInputStream
        Oferece métodos unread() que permitem repor um ou mais bytes de volta à sua fonte, como se eles não tivessem sido lidos;

    java.io.ObjectInputStream
    Oferece método readObject() para a leitura de objetos que foram serializados para um ObjectOutputStream;


    java.io.PipedInputStream
    Oferece a funcionalidade de leitura de um pipe de bytes cuja origem está associada a um objeto PipedOutputStream; 

    java.io.SequenceInputStream
    Oferece a funcionalidade para concatenar dois ou mais objetos InputStream; o construtor especifica os objetos que serão concatenados e, automaticamente, quando o fim do primeiro objeto é alcançado os bytes passam a ser obtidos do segundo objeto.

    Isso é realizado porque não é possível transferir tudo ao mesmo tempo, entre a memória do programa e o arquivo, ou ler do arquivo para memória instantaneamente.

    Arquivos binários são aqueles que usam todo o conjunto de números para representar uma informação, por exemplo, uma foto, uma planilha, um arquivo executável.

    Existe ainda os arquivos contendo apenas caracteres, ou seja, letras, números e alguns caracteres especiais para representar algo na tela ou na impressora. Esses arquivos não precisam de todos os números possíveis para representar suas informações. Sendo assim, eles sãi um caso especial e podem ser trabalhados mais facilmente caractere a caractere, ao invés de número a número. A esses arquivos, damos o nome de arquivo-texto.

    Saídas de seqüências de bytes são tratadas por OutputStream e suas classes derivadas.

    3. OutputStream

    As principais subclasses de OutputStream no pacote java.io são:

    java.io.ByteArrayOutputStream
        Facilidades para escrever para um arranjo de bytes interno, que cresce de acordo com a necessidade e pode ser acessado posteriormente através do método toByteArray() ou toString(); 

    java.io.FileOutputStream
        Facilidades para escrever em arquivos, usualmente utilizadas em conjunção com as classes BufferedOutputStream e DataOutputStream; 

    java.io.FilterOutputStream
        Definição de funcionalidades básicas para a filtragem de saída de dados, implementadas em alguma de suas classes derivadas:

    java.io.BufferedOutputStream
        Armazena bytes em um buffer interno até que o buffer esteja cheio ou o método flush() seja invocado; 

    java.io.DataOutputStream
        Permite escrever valores de variáveis de tipos primitivos de Java em um formato binário portátil. É uma implementação da interface DataOutput.; 

    java.io.PrintStream
        Oferece métodos para apresentar representações textuais dos valores de tipos primitivos Java, através de métodos print() e println();

    java.io.ObjectOutputStream
        Permite armazenar a representação de um objeto serializável em um OutputStream; 

    java.io.PipedOutputStream
        Implementa a origem de um pipe de bytes, que serão lidos a partir de um objeto PipedInputStream. 

    4. Exemplo das classes Reader e Writer

    Observe que as funcionalidades de transferência seqüencial de dados a partir de ou para um arquivo não é suportada pela classe File, mas sim pelas classes FileInputStream, FileOutputStream, FileReader e FileWriter. Todas estas classes oferecem pelo menos um construtor que recebe como argumento um objeto da classe File e implementam os métodos básicos de transferência de dados suportados respectivamente por InputStream, OutputStream, Reader e Writer.

    Observe os código a seguir:

 import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class Leitor {
public static String ler(File arquivo) {
StringBuffer sb = new StringBuffer();
try {
FileReader reader = new FileReader(arquivo);
int c;
do {
c = reader.read();
if (c!=-1) {
sb.append( (char)c );
}
} while (c != -1);
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
}
    A estrutura de bloco chamada try-catch, serve para capturar um erro, se algo por ventura, devido ao acesso a dispositivo de disco.
   Quando chegar ao final da leitura do último caracter, o valor será igual a -1, o laço de repetição terminará.
    Depois do processo de leitura terminar, o arquivo será fechado se isto não for feito o arquivo estará travado .
 import java.io.File;
import javax.swing.JOptionPane;

public class UsoLeitor {
public static void main(String[] args) {
File arquivo = new File("MeuTexto.txt");
String s = Leitor.ler(arquivo);
JOptionPane.showMessageDialog( null, s, "Uso Leitor", JOptionPane.INFORMATION_MESSAGE );

}
}

   

    O programa é executado através da classe UsoLeitor.

 import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class Escritor {
public static void escreve(File arquivo, String texto) {
try {
FileWriter writer = new FileWriter(arquivo);
char [] c = texto.toCharArray();
for (int cont=0; cont<c.length; cont++) {
writer.write(c[cont]);
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}



    O método estático contido na classe Escritor, recebe uma referência ao arquivo e um texto a ser gravado. 


import java.io.File;
import javax.swing.JOptionPane;

public class UsoEscritor {
public static void main(String[] args) {
String nome_arquivo = JOptionPane.showInputDialog("Entre com o nome do arquivo");
File arquivo = new File(nome_arquivo);

String texto = JOptionPane.showInputDialog("Entre com um texto pequeno");
Escritor.escreve(arquivo, texto);
}
}

    O exemplo a seguir demonstra como podemos realizar cópias de arquivos, utilizando uma classe File do Java.

 import java.io.File;
import javax.swing.JOptionPane;

public class Copiador {
public static void main(String[] args) {
String arquivo1 = JOptionPane.showInputDialog("Entre com o nome do arquivo a ser copiado");
File entrada = new File(arquivo1);
String arquivo2 = JOptionPane.showInputDialog("Entre com o nome do arquivo copiado");
File saida = new File(arquivo2);
String s = Leitor.ler(entrada);
Escritor.escreve(saida, s);
}

}

    
    A classe Copiador, atende bem ao propósito de leitura de arquivos textos e cópia dos mesmos. O problema é que não funcionará para um arquivo binário, como uma imagem, por exemplo.

    Arquivos binários possuem uma variedade de valores binários, que um arquivo texto não possui. 

    Neste caso, será necessário um programa que leia arquivos binários e não caracteres, como també, escrever arquivos binários em disco. Veja o exemplo a seguir:

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopiadorBinario {
public static void copia(File entrada, File saida) {
try {
FileInputStream input = new FileInputStream(entrada);
FileOutputStream output = new FileOutputStream(saida);
int c;
do {
c = input.read();
if (c!=-1) {
output.write(c);
}
} while (c != -1);
input.close();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

     O programa contido na classe UsoCopiadorBinario, utiliza a classe CopiadorBinario como mostra o código a seguir:

 import java.io.File;
import javax.swing.JOptionPane;

public class UsoCopiadorBinario {
public static void main(String[] args) {
String arquivo = JOptionPane.showInputDialog("Entre com o nome do arquivo a ser copiado");
File entrada = new File(arquivo);
String arquivo_saida = JOptionPane.showInputDialog("Entre com o nome da copia do arquivo");
File saida = new File(arquivo_saida);
CopiadorBinario.copia(entrada, saida);
JOptionPane.showMessageDialog( null, "Cópia concluida", "Copia", JOptionPane.INFORMATION_MESSAGE );
}
}

    5. Buffers

    Essencialmente, um buffer é uma entidade intermediária entre duas ou mais outras entidades que produzem e consomem elementos. Internamente, um buffer contém uma área de memória que é utilizada para armazenamento temporário de elementos que foram produzidos mas ainda não foram consumidos.

Buffer

    Essa capacidade de um buffer manter temporariamente parte dos dados produzidos mas não consumidos torna-o um mecanismo adequado para compatibilizar a operação de entidades operando em diferentes velocidades. Além de oferecer transparentemente as mesmas funcionalidades de leitura e escrita de dados, um buffer normalmente oferece operações adicionais que permitem consultar se há dados disponíveis para leitura no buffer de entrada ou para solicitar que os dados presentes no buffer de saída sejam enviados imediatamente para o dispositivo de saída.

    Em Java, há quatro classes que adicionam as funcionalidades de buffering às classes fundamentais de entrada e saída. Para classes que manipulam bytes, as classes BufferedInputStream e BufferedOutputStream são oferecidas. Para a manipulação de entrada e saída de texto, as classes BufferedReader e BufferedWriter.

    Observe a seguir o programa demonstrando BufferedReader e BufferedWrite:

 import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class LeitorBuffer {
public static String ler(File arquivo) {
StringBuffer sb = new StringBuffer();
try {
FileReader reader = new FileReader(arquivo);
BufferedReader bufReader = new BufferedReader(reader);
String s;
do {
s = bufReader.readLine();
if (s!=null) {
sb.append( s + "\r\n" );
}
} while (s!=null);
bufReader.close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
}

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class EscritorBuffer {
public static void escreve(File arquivo, String texto) {
try {
FileWriter writer = new FileWriter(arquivo);
BufferedWriter bufWriter = new BufferedWriter(writer);
bufWriter.write(texto);
bufWriter.close();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

import java.io.File;
import javax.swing.JOptionPane;

public class CopiadorBuffer {
public static void main(String[] args) {
String arquivo_entrada = JOptionPane.showInputDialog("Entre com o nome do arquivo de entrada");
File entrada = new File(arquivo_entrada);
String arquivo_saida = JOptionPane.showInputDialog("Entre com o nome do arquivo de saida");
File saida = new File(arquivo_saida);
String s = LeitorBuffer.ler(entrada);
EscritorBuffer.escreve(saida, s);
}
}

  6. Exemplo Java Swing


 import java.awt.Container;
import java.io.File;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class LeitorSwing {
public static void main(String[] args) {
JFrame frame = new JFrame("Javanoroeste");
JTextArea textArea = new JTextArea();
JScrollPane scrollPane = new JScrollPane(textArea);
Container container = frame.getContentPane();
container.add(scrollPane);
frame.setSize(500, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
String arquivo_ler = JOptionPane.showInputDialog("Entre com o nome do arquivo de saida");
File arquivo = new File(arquivo_ler);
String s = LeitorBuffer.ler(arquivo);
textArea.append ( s );
}
}

 import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class LeitorSwingComplexo {
private static JTextArea textArea;
private static JFrame frame;

public static void main(String[] args) {
frame = new JFrame("Javanoroeste");
JPanel p = new JPanel(new BorderLayout());
textArea = new JTextArea();
JScrollPane scrollPane = new JScrollPane(textArea);
p.add(scrollPane, BorderLayout.CENTER);
JButton button = new JButton("Abrir Arquivo");
button.addActionListener(new AcaoLeitura());
p.add(button, BorderLayout.SOUTH);
Container container = frame.getContentPane();
container.add(p);
frame.setSize(500, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}

static class AcaoLeitura implements ActionListener {
public void actionPerformed(ActionEvent arg0) {
JFileChooser fileChooser = new JFileChooser(".");
fileChooser.showOpenDialog(frame);
File arquivo = fileChooser.getSelectedFile();
if (arquivo!=null) {
textArea.setText("");
String s = LeitorBuffer.ler(arquivo);
textArea.append ( s );
}
}
}
}

Autores: Carlos Fernando Gonçalves e Marli Esprega Gonçalves (Antares Information Systems)