Introdução
Saudações aos guerreiros da programação. Estamos aqui com mais um post e dessa vez vamos abordar a leitura e escrita de arquivos utilizando os recursos fornecidos pelo Java em sua versão 7. Lembrando que estou aberto a criticas e comentários, acredito que é trocando idéias e experiencia que crescemos, portanto não deixem de expressar sua opinião. E mãos a obra.
A API de I/O do Java
Trata-se dos recursos fornecidos pelo Java para trabalhar com fluxo de Entrada
e Saída de dados através de meios como console, arquivos e socket’s. O Fluxo acontece basicamente de duas formas: Leitura de
dados (InputStream) e Escrita de
dados (OutputStream). O Java trabalha
com stream aplicando polimorfismo de
forma a ocultar a origem e destino dos dados, ou seja, para alguns métodos,
independente da fonte dos dados, a sintaxe para execução será a mesma. Segue
abaixo um exemplo do uso da classe para leitura de bytes em um arquivo passado como parâmetro:
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
public class IO {
public void AbrirArquivo(String arquivo){
try{
InputStream
entrada = new FileInputStream(arquivo);
int b = entrada.read();
}catch(IOException e){
System.out.println("Error: " + e.getMessage() + "\n" + " Não foi possível abrir o arquivo");
}
}
O mesmo exemplo para
leitura no console:
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
public class IO {
public void lerConsole{
try{
InputStream
entrada = System.in;
int b = entrada.read();
}catch(IOException e){
System.out.println("Error: " + e.getMessage() + "\n" + " Não foi possível abrir o arquivo");
}
}
}
O processo para
escrita de dados é semelhante mudando apenas a classe de stream que no caso é OutputStream.
Observe abaixo exemplo de escrita em um arquivo qualquer:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class IO {
private OutputStream saida;
public void escreverArquivo(String arquivo){
try{
saida = new FileOutputStream(arquivo);
saida.write(20);
}catch(IOException e){
System.out.println("Error: " + e.getMessage() + "\n" + " Não foi possível escrever no arquivo");
}
}
}
O mesmo exemplo para
escrita no console:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class IO {
public void escreverConsole(){
try{
OutputStream
saida;saida = System.out;
saida.write(20);
}catch(IOException e){
System.out.println("Error: " + e.getMessage() + "\n" + " Não foi possível escrever no arquivo");
}
}
}
Perceba que independente do método de entrada será sempre executado “read” para leitura do arquivo, da mesma
forma na escrita de dados, independente do destino será sempre executado “write” para escrita.
É importante saber que a forma do
Java para trabalhar com os stream’s
só ocorre em bytes, isto significa que para trabalhar com dados mais robustos
como caracteres será necessário um tratamento específico. Para realizar este
tipo de tratamento é necessário aplicar uma classe que funcionará como ponte
para converter os bytes em caracteres. Esta classe é a InputStremReader. Veja abaixo como funciona:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class IO {
public void AbrirArquivo(String arquivo){
try{
InputStream
entrada = new FileInputStream(arquivo);
InputStreamReader leitura
= new InputStreamReader(entrada);
char c = (char)
leitura.read();
}catch(IOException
e){
System.out.println("Error: " +
e.getMessage() + "\n" + " Não foi possível abrir o arquivo");
}
}
}
Perceba que a InputStreamReader
recebe um arquivo de InputStrem para
leitura e através do cast para char é
atribuído o valor a variável “c” para posterior utilização. Acontece que
evetualmente ler caracter a caracter de um arquivo pode não ser uma boa idéia,
dessa forma é necessário um recurso que viabilize a leitura de uma linha
inteira de do arquivo e atribua o valor a uma String. A classe capaz de fazer
esse trabalho é a BufferedReader.
Para exemplificar a sua utilização vamos pegar o ultimo exemplo onde estamos
com o objeto de leitura do arquivo “fonte.txt” podemos fazer a leitura pelo BufferedReader:
import
java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class IO {
public void AbrirArquivo(String arquivo){
try{
InputStream
entrada = new FileInputStream(arquivo);
InputStreamReader
leitura = new InputStreamReader(entrada);
BufferedReader
buff = new BufferedReader(leitura);
String
s = buff.readLine();
}catch(IOException e){
System.out.println("Error: " + e.getMessage() + "\n" + " Não foi possível abrir o arquivo");
}
}
}
Dessa forma percebemos uma certa hierarquia no processo de leitura, onde
começamos no mais baixo nível, como byte, e subimos até o nível mais alto da String
respeitando a seguinte ordem:
- InputStrem: bytes.
- InputStreamReader: caracteres.
- BufferedReader: Strings.
O processo para escrita é semelhante mudando apenas o nome das classes
envolvidas segundo a ordem abaixo:
- OutputStream: bytes.
- OutputStreamWriter: caracteres.
- BufferedWriter: Strings.
Assim, um exemplo para o procedimento seria:
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class IO {
public void escreverArquivo(String arquivo){
try{
OutputStream
esc = new FileOutputStream(arquivo);
OutputStreamWriter
escrita = new OutputStreamWriter(esc);
BufferedWriter
bw = new BufferedWriter(escrita);
bw.write(123);
}catch(IOException e){
System.out.println("Error: " + e.getMessage() + "\n" + " Não foi possível escrever no arquivo");
}
}
}
FileReader e FileWriter
Você deve ter percebido até aqui que existem classes demais para se trabalhar com arquivos de texto, deixando o processo de certo modo um pouco confuso. Foi pensando em minimizar isso que foram criadas as classes FileReader e FileWriter para leitura e escrita de caracteres respectivamente. A forma de instanciar estas classes segue o mesmo padrão das demais e utilizá-las além de ser mais simples torna o código mais legível. No exemplo abaixo elas são utilizadas juntamente com as classes BufferedReader e BufferedWriter, pois como visto anteriormente, estas trabalham com strings. Observe:
Para exemplificar criei uma classe de nome Arquivo que instancia um objeto do tipo de File e faz uso dos seus métodos para atender a chamadas de métodos próprios na classe Arquivo. Observe o código abaixo:
Em seguida uma pequena aplicação para fazer uso de nossa classe Arquivo:
FileReader e FileWriter
Você deve ter percebido até aqui que existem classes demais para se trabalhar com arquivos de texto, deixando o processo de certo modo um pouco confuso. Foi pensando em minimizar isso que foram criadas as classes FileReader e FileWriter para leitura e escrita de caracteres respectivamente. A forma de instanciar estas classes segue o mesmo padrão das demais e utilizá-las além de ser mais simples torna o código mais legível. No exemplo abaixo elas são utilizadas juntamente com as classes BufferedReader e BufferedWriter, pois como visto anteriormente, estas trabalham com strings. Observe:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class IO {
public void
escreverArquivo(String arquivo){
try{
FileWriter
w = new FileWriter(arquivo);
BufferedWriter
bw = new BufferedWriter(w);
bw.write("Olá mundo");
}catch(Exception e){
System.out.println("Error: " + e.getMessage());
}
}
public void lerArquivo(String arquivo){
try{
FileReader
r = new FileReader(arquivo);
BufferedReader
br = new BufferedReader(r);
String s = br.readLine();
}catch(Exception e){
System.out.println("Error: " + e.getMessage());
}
}
}
Perceba que a classe FileReader substitui as classes InputStream e InputStreamReader, e da mesma forma acontece com FileWriter e as classes OutputStream e OutputStreamWriter. Lembrando que as mesmas trabalham apenas com arquivos de texto diferentemente dos pares de Stream que trabalham byte a byte independente do tipo do dado.
Scanner e PrintStream
São outras duas opções para se trabalhar com Stream. Estas duas são
ainda mais simples de se utilizar que as demais, tanto pela simplicidade quanto
pela clareza do código. Tal qual nos exemplos anteriores estas duas classes
trabalham aos pares onde,
Scanner: lê dados de uma Stream de entrada.
PrintStream: escreve dados em uma Stream de saída.
Veja abaixo um exemplo de utilização da classe Scanner:
import java.io.FileInputStream;
import java.util.Scanner;
public class IO {
@SuppressWarnings("finally")
public String lerArquivo(String
arquivo){
StringBuilder
texto = new StringBuilder("");
try{
Scanner
s = new Scanner(new FileInputStream(arquivo));
while (s.hasNextLine()){
texto.append(s.nextLine());
texto.append("\n");
}
}catch(Exception e){
System.out.println("Error: Falha ao abrir
arquivo");
}finally{
return texto.toString();
}
}
}
Esta classe possui um método que retorna em uma String o conteúdo de um arquivo passado como parâmetro. Para este exemplo criei um arquivo de nome "entrada.txt" e salvei na raiz da unidade C com um conteúdo qualquer. Para demonstrar a utilização da classe foi criado a classe com o método main conforme descrito a seguir:
public class App {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method
stub
IO
a = new IO();
System.out.println(a.lerArquivo("C:/entrada.txt"));
}
}
Agora um exemplo da utilização da classe PrintStream:
import java.io.FileOutputStream;
import java.io.PrintStream;
public class IO {
@SuppressWarnings("finally")
public boolean escreverArquivo(String arquivo, String
msg){
boolean retorno = false;
try{
PrintStream
ps = new PrintStream(new FileOutputStream(arquivo));
ps.print(msg);
retorno
= true;
}catch(Exception e){
System.out.println("Error: Não foi possivel escrever
no arquivo");
}finally{
return retorno;
}
}
}
Nesta classe criada perceba que ela possui um método escreverArquivo justamente para realizarmos a demonstração. Este método recebe dois parâmetros do tipo String, um é o destino da Stream e o outro, o conteúdo. É interessante notar que a classe PrintStream possui os métodos print() e println(), não por mera coincidência, os métodos são idênticos aos que utilizamos na classe System.out. Isto ocorre pelo fato de System.out ser uma PrintStream. Por fim, o método retorna true caso tenha ocorrido tudo bem. Veja a utilização da classe em uma pequena aplicação:
public class LerArquivo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method
stub
IO
a = new IO();
System.out.println(a.escreverArquivo("C:/entrada.txt", "Alo Mundo!"));
}
}
A Classe java.io.File
A classe File é muito útil para representar, em forma de objeto, arquivos e pastas do sistema. Uma vez instanciado, através de seus métodos é possível obter praticamente todas as informações necessárias. Alguns destes métodos são:
isDiretory() informa se é arquivo ou diretório
exists() informa se o arquivo ou diretório existe
getName() obtém o nome do arquivo ou diretório
getPath() obtém o caminho completo do arquivo ou diretório
listFiles() lista os arquivos de um diretório
isDiretory() informa se é arquivo ou diretório
exists() informa se o arquivo ou diretório existe
getName() obtém o nome do arquivo ou diretório
getPath() obtém o caminho completo do arquivo ou diretório
listFiles() lista os arquivos de um diretório
Para exemplificar criei uma classe de nome Arquivo que instancia um objeto do tipo de File e faz uso dos seus métodos para atender a chamadas de métodos próprios na classe Arquivo. Observe o código abaixo:
import java.io.File;
public class Arquivo {
public File arquivo;
public Arquivo(String arquivo){
this.arquivo = new
File(arquivo);
}
public boolean isDiretorio(){
return this.arquivo.isDirectory();
}
public boolean existe(){
return this.arquivo.exists();
}
public String getNome(){
return this.arquivo.getName();
}
public String getCaminho(){
return this.arquivo.getPath();
}
public File[] lisArquivos(){
return this.arquivo.listFiles();
}
}
import java.io.File;
public class App {
public static void main(String[] args){
Arquivo arq = new Arquivo("C:/temp/");
System.out.println(arq.isDiretorio());
System.out.println(arq.getCaminho());
File[]
f;
f
= arq.lisArquivos();
for (File x : f){
System.out.println(x.getName());
}
}
}
Observe no código da aplicação que fiz um pequeno trabalho para listar o nome dos arquivos dentro da pasta fornecida na instanciação do arquivo. Como o método lisArquivo() que faz uso do método listFiles() da classe File, retorna um vetor com nome de arquivos, então tive de utilizar uma estrutura de laço para obter o nome de cada arquivo na pasta. Eu poderia ainda Alterar o metodo lisArquivo() para retornar a string com os nomes de arquivos pronta para exibição no console, mas essa opção pode ficar como exercício a você caro leitor.
Try-with-resources
Quando trabalhamos com Stream seja para ler ou escrever, é necessário abrir o arquivo através de meios já demostrado neste mesmo post. O fato é que todo arquivo aberto precisa ser fechado. O método correspondente a este procedimento é o close(). Nas ultimas versões do java o procedimento era feito acrescentando-se no bloco finally do try-cath, a chamada ao método. No entanto a partir da versão 7 do java dispomos do recurso try-with-resources no qual basta declarar a leitura do arquivo entre parenteses do lado da palavra chave try que assim, após a execução do bloco de código, o java automaticamente executa o método close() do arquivo aberto. Neste exemplo demonstrarei o método mais comum :
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
public class IO {
public void AbrirArquivo(String arquivo){
InputStream entrada = null;
try {
entrada
= new FileInputStream(arquivo);
int b = entrada.read();
}catch(IOException e){
System.out.println("Error: " + e.getMessage() + "\n" + " Não foi possível abrir o
arquivo");
}finally{
if (entrada != null){
try {
entrada.close();
}
catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Error: não foi possivel fechar arquivo");
}
}
}
}
}
No exemplo acima utilizamos o bloco finally para que o método tente fechar o arquivo de qualquer maneira lançando uma mensagem de erro no console caso algo não funcione como esperado. Mas como dito anteriormente, com o novo recurso do java, o mesmo código pode ser escrito assim:
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
public class IO {
public void AbrirArquivo(String arquivo){
try (InputStream entrada = new FileInputStream(arquivo)){
int b = entrada.read();
}catch(IOException e){
System.out.println("Error: " + e.getMessage() + "\n" + " Não foi possível abrir o arquivo");
}
}
}
Finalizando
Bem pessoal, chegamos ao final de mais um post. Vimos um pouco da API de IO do java, as classes stream, como trabalhar com elas, alternativas de uso. Espero que tenha sido útil e meu desejo como sempre é que este possa servir como um ponto de partida, um ponta-pé inicial na busca do aprendizado. Um forte abraço, fiquem com Deus e até a próxima.
Nenhum comentário:
Postar um comentário