Pontifícia Universidade Católica de São Paulo Departamento de Ciência da Computação Apontamento 15 Maio de 2004 LP: Laboratório de Programação Prof. ISVega Arquivos de Texto CONTEÚDO 15.1 Processamento de Arquivos . . . . . . . . . . . 15.2 Processamento de Arquivos de Texto . . . . . 15.2.1Escrita de Texto COM Filtros . . . . . . 15.2.2Leitura de Texto COM Filtros . . . . . . 15.3 Aplicação: Controle de Estoque . . . . . . . . 15.3.1Criação do Arquivo de Estoque . . . . . 15.3.2Processamento do Arquivo de Estoque Exercícios . . . . . . . . . . . . . . . . . . . . . . . . Objetivos . . . . . . . . . . . . . . . . 1 2 3 6 8 9 11 15 • Mostrar como ler e escrever arquivos de texto em Java. 15.1 Processamento de Arquivos Uma necessidade comum de programação é a leitura e escrita de arquivos em disco. As informações armazenadas em um objeto-arquivo ou são codificadas na forma de bytes (dados) ou na forma de texto (linhas de caracteres). 1 Laboratório de Programação Maio de 2004 Arquivos Pré-definidos Três objetos-arquivo para armazenagem de textos são automaticamente criados, quando se executa um programa em Java: System.in, System.out e System.err. Subsistema java.io Para fazer uso dos mecanismos de processamento de arquivos em Java, deve-se importar o subsistema (pacote) java.io. Exemplo 15.1 Para que um objeto da classe Livro tenha acesso aos mecanismos de processamento de arquivos da Tecnologia Java, a sua classe deverá importar o pacote java.io (Figura 15.1). Livro <<import>> java.io Figura 15.1: Importação do pacote java.io (diagrama de classes UML). Em Java: import java.io.*; public class Livro { // Uso dos mecanismos importados... } ¨ Textos e Dados Por outro lado, as seqüências de bytes podem ser vistas como texto ou como dados (estruturados). No primeiro caso, deve-se utilizar instâncias do tipo Reader e Writer. No segundo caso, deve-se usar instâncias do tipo FilterInputStream e FilterOutputStream. 15.2 Processamento de Arquivos de Texto Processamento de Texto com Filtros O processamento de arquivos de texto (caracteres unicode) com filtros é feito por objetos da classe BufferedReader e BufferedWriter. O arquivo, propriamente dito, é representado por objetos das classes Reader ou Writer. c Copyright °1998-2004, Dr. Italo S. Vega 15-2 Laboratório de Programação Maio de 2004 15.2.1 Escrita de Texto COM Filtros O padrão dinâmico da escrita em arquivos de texto filtros pode ser descrito pelo mapa de execução mostrado na Figura 15.2. Programa “dados.txt” arqTexto : FileWriter new dados.txt arqTexto texto new write filtro : BufferedWriter flush close Figura 15.2: Padrão para escrita em arquivos de texto. Inicialmente, cria-se um contexto de execução que interage diretamente com o arquivo, no caso dados.txt. Este contexto é utilizado para construir o filtro de acesso ao conteúdo do arquivo. O filtro é um contexto da classe BufferedWriter. Com a ajuda do filtro, diversas linhas de texto podem ser escritas no arquivo utilizando-se a operação write(). Este serviço recebe a linha de texto a ser escrita, repassando-a para o contexto de execução que interage diretamente com o arquivo. Imediatamente antes de se fechar o filtro (que, por sua vez, provoca o fechamento do arquivo), deve-se executar o comando flush(), que envia quaisquer linhas de texto ainda presentes no filtro para o arquivo. Observa-se que todas as interações com o arquivo e com o filtro devem ser inseridas em uma região de tratamento de exceções (assinaladas como “relâmpagos” em vermelho na Figura 15.2). Modelo de Implementação em Java A Figura 15.3 ilustra um modelo de processamento de arquivos de texto em Java. c Copyright °1998-2004, Dr. Italo S. Vega 15-3 Laboratório de Programação Maio de 2004 Figura 15.3: Modelo de objetos para escrita de arquivos de texto (vista do BlueJ). Um modelo de implementação em Java deste modelo de escrita em arquivos de texto seria: hlab/texto/EscritaTextoFiltro.javai≡ hTexto: (escrita com filtro) classes importadasi public class EscritaTextoFiltro { public BufferedWriter filtro; public void escrever() { hTexto: (escrita com filtro) abertura e criação do filtroi hTexto: (escrita com filtro) escrita no arquivoi hTexto: (escrita com filtro) fechamento do arquivoi } } Importação de Classes para Escrita As seguintes classes são normalmente importadas quando se programa computações com escrita em arquivos de texto: hTexto: (escrita com filtro) classes importadasi≡ import java.io.FileWriter; import java.io.BufferedWriter; import java.io.IOException; Abertura do Arquivo de Texto O processamento se inicia com a abertura do arquivo de texto e a criação do filtro a ser utilizado para processar a sua informação: c Copyright °1998-2004, Dr. Italo S. Vega 15-4 Laboratório de Programação Maio de 2004 hTexto: (escrita com filtro) abertura e criação do filtroi≡ // ABRIR ARQUIVO DE TEXTO E CRIAR FILTRO try { FileWriter arqTexto = new FileWriter( "dados.txt" ); filtro = new BufferedWriter( arqTexto ); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } Escrita no Arquivo de Texto Em seguida, passa-se à escrita de linhas de texto no arquivo. Strings podem ser escritas com o uso da operação write(). Mudanças de linha no arquivo podem ser feitas ativando-se a operação newLine(): hTexto: (escrita com filtro) escrita no arquivoi≡ // ESCREVER NO ARQUIVO DE TEXTO try { filtro.write( "Isto eh um teste!" ); filtro.newLine(); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } Fechamento do Arquivo de Texto Finalmente, após a escrita das informações no arquivo, deve-se fechá-lo, para que o texto não seja perdido: hTexto: (escrita com filtro) fechamento do arquivoi≡ // ESVAZIAR FILTRO E FECHAR O ARQUIVO DE TEXTO try { filtro.flush(); filtro.close(); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } c Copyright °1998-2004, Dr. Italo S. Vega 15-5 Laboratório de Programação Maio de 2004 15.2.2 Leitura de Texto COM Filtros Os principais passos de computação envolvidos na leitura de um texto armazenado em arquivos de texto são apresentados na Figura 15.4. Programa “dados.txt” arqTexto : FileReader new arqTexto dados.txt new readLine filtro : BufferedReader texto close Figura 15.4: Padrão para leitura de arquivos de texto. Modelo de Implementação em Java A Figura 15.5 ilustra um modelo de processamento de arquivos de texto em Java. Figura 15.5: Modelo de objetos para leitura de arquivos de texto (vista do BlueJ). Um modelo de implementação em Java deste modelo de leitura em arquivos de texto seria: hlab/texto/LeituraTextoFiltro.javai≡ hTexto: (leitura com filtro) classes importadasi public class LeituraTextoFiltro { c Copyright °1998-2004, Dr. Italo S. Vega 15-6 Laboratório de Programação Maio de 2004 public BufferedReader filtro; public void ler() { hTexto: (leitura com filtro) abertura e criação do filtroi hTexto: (leitura com filtro) leitura no arquivoi hTexto: (leitura com filtro) fechamento do arquivoi } } Importação de Classes para Leitura As seguintes classes são normalmente importadas quando se programa computações com leitura de arquivos de texto: hTexto: (leitura com filtro) classes importadasi≡ import java.io.FileReader; import java.io.BufferedReader; import java.io.IOException; import java.io.FileNotFoundException; Abertura do Arquivo de Texto O processamento se inicia com a abertura do arquivo de texto e a criação do filtro a ser utilizado para processar a sua informação: hTexto: (leitura com filtro) abertura e criação do filtroi≡ // ABRIR ARQUIVO DE TEXTO E CRIAR FILTRO try { FileReader arqTexto = new FileReader( "dados.txt" ); filtro = new BufferedReader( arqTexto ); } catch( FileNotFoundException e ) { System.out.println( e ); System.exit( 1 ); } Leitura do Arquivo de Texto c Copyright °1998-2004, Dr. Italo S. Vega 15-7 Laboratório de Programação Maio de 2004 Em seguida, passa-se à leitura de linhas de texto do arquivo com a ativação da operação readLine(): hTexto: (leitura com filtro) leitura no arquivoi≡ // LER DO ARQUIVO DE TEXTO try { String linha = filtro.readLine(); System.out.println( linha ); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } Fechamento do Arquivo de Texto Finalmente, após a leitura das informações no arquivo, deve-se fechá-lo: hTexto: (leitura com filtro) fechamento do arquivoi≡ // FECHAR O ARQUIVO DE TEXTO try { filtro.close(); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } 15.3 Aplicação: Controle de Estoque Esta seção cria um arquivo contendo produtos, quantidades e preços unitários, o qual é posteriormente processado, produzindo um relatório de estoque. O modelo de classes da aplicação, bem como dos objetos fundamentais, é descrito na Figura 15.6. c Copyright °1998-2004, Dr. Italo S. Vega 15-8 Laboratório de Programação Maio de 2004 Figura 15.6: Importação do pacote java.io (diagrama de classes UML). 15.3.1 Criação do Arquivo de Estoque A criação do arquivo de estoque, denominado estoque.txt, segue o padrão anteriormente descrito para o caso de arquivos de texto. hlab/estoque/Estoquista.javai≡ hpacotes importados na criação do estoquei public class Estoquista { public BufferedWriter filtro = null; public void cadastrar() { habrir o arquivo de texto e criar o filtro de escrita do estoquei hobter as quantidades de produtos e escrever no arquivoi hesvaziar o filtro e fechar o arquivo de textoi System.exit(0); } } São os seguintes as classes necessárias para criar o arquivo de estoque: hpacotes importados na criação do estoquei≡ import java.io.FileWriter; import java.io.BufferedWriter; import java.io.IOException; A abertura e criação do filtro de escrita obedece ao padrão: c Copyright °1998-2004, Dr. Italo S. Vega 15-9 Laboratório de Programação Maio de 2004 habrir o arquivo de texto e criar o filtro de escrita do estoquei≡ // ABRIR ARQUIVO DE TEXTO E CRIAR FILTRO BufferedWriter filtro = null; try { FileWriter arqTexto = new FileWriter( "estoque.txt" ); filtro = new BufferedWriter( arqTexto ); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } A parte principal da criação do arquivo de estoque deve percorrer cada produto e solicitar que o estoquista informe a quantidade atual existente. Inicialmente os produtos e preços unitários são definidos. Em seguida, cria-se um optico:LeitorOptico para solicitação das quantidades. Uma repetição controlada por contador percorre cada produto e solicita a quantidade existente. Este valor é convertido para int, sendo utilizado para montar uma linha, escrita no arquivo de texto: hobter as quantidades de produtos e escrever no arquivoi≡ // ESCREVER NO ARQUIVO DE TEXTO try { String[] produtos = { "Lapis", "Borracha", "Caderno", "Caneta" }; String[] precos = { "0.70", "0.35", "5.00", "1.20" }; LeitorOptico optico = new LeitorOptico(); for( int i = 0; i < produtos.length; i++ ) { int qtde = optico.obterInt( "Quantidade de " + produtos[i] + "?"); String linha = produtos[i] + " " + qtde + " " + precos[i]; filtro.write( linha ); filtro.newLine(); } optico = null; } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } A parte final esvazia o filtro e fecha o arquivo de texto contendo as informações sobre o estoque atual: c Copyright °1998-2004, Dr. Italo S. Vega 15-10 Laboratório de Programação Maio de 2004 hesvaziar o filtro e fechar o arquivo de textoi≡ // ESVAZIAR FILTRO E FECHAR O ARQUIVO DE TEXTO try { filtro.flush(); filtro.close(); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } O contexto envolvendo o leitor óptico apenas solicita a quantidade de produto disponível no estoque, simulando o dispositivo real de leitura utilizado pelo estoquista: hlab/estoque/LeitorOptico.javai≡ import javax.swing.JOptionPane; public class LeitorOptico { public int obterInt( String pergunta ) { String linha = JOptionPane.showInputDialog( pergunta ); int n = 0; try { n = Integer.parseInt( linha ); } catch( NumberFormatException e ) { System.out.println( "Quantidade desconhecida" ); } return n; } } 15.3.2 Processamento do Arquivo de Estoque O processamento do arquivo de estoque deve ler os valores informados e gerar um relatório da situação atual dos produtos. hlab/estoque/Estoque.javai≡ hclasses importadas para o processamento do estoquei public class Estoque { public Item[] produtos = new Item[ 100 ]; public void recuperarItens() { habrir o arquivo de texto e criar o filtro de leitura do estoquei hcarregar as informações sobre cada produto e gerar o relatórioi hfechar o arquivo de estoquei } hmostrar o relatório do estoquei } São os seguintes os arquivos importados para o processamento do estoque: c Copyright °1998-2004, Dr. Italo S. Vega 15-11 Laboratório de Programação Maio de 2004 hclasses importadas para o processamento do estoquei≡ import java.io.FileReader; import java.io.BufferedReader; import java.io.IOException; import java.io.FileNotFoundException; import java.util.StringTokenizer; A abertura do arquivo de estoque para leitura, segue o padrão normal visto anteriormente: habrir o arquivo de texto e criar o filtro de leitura do estoquei≡ // ABRIR ARQUIVO DE TEXTO E CRIAR FILTRO BufferedReader filtro = null; try { FileReader arqTexto = new FileReader( "estoque.txt" ); filtro = new BufferedReader( arqTexto ); } catch( FileNotFoundException e ) { System.out.println( e ); System.exit( 1 ); } A carga de informações do arquivo faz uso do filtro de leitura de linhas. Para cada linha recuperada, o produto, sua quantidade e preço unitário devem ser determinados. Um contexto denominado analisador da classe StringTokenizer pode ser utilizado para esta finalidade. O analisador é criado para cada linha. O serviço nextToken() retorna a cadeia de caracteres que antecede um separador (o espaço e o terminador de linha são os separadores normais). No caso deste sistema, cada linha contém três informações (produto, quantidade e preço) separaradas por um espaço. Por conseguinte três ativações do serviço nextToken() são utilizadas para recuperar estas informações. A partir delas, um novo contexto da classe Item é construído, sendo de imediato inserido na lista produtos, representado o estoque atual: c Copyright °1998-2004, Dr. Italo S. Vega 15-12 Laboratório de Programação Maio de 2004 hcarregar as informações sobre cada produto e gerar o relatórioi≡ // LER DO ARQUIVO DE TEXTO Item[] produtos = new Item[ 100 ]; int n = 0; try { String linha = filtro.readLine(); while( linha != null ) { StringTokenizer analisador = new StringTokenizer( linha ); String produto = analisador.nextToken(); hrecuperar um item e inserir na lista de produtosi linha = filtro.readLine(); } } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } No núcleo da estrutura de repetição controlada por sentinela, deve-se recuperar as informações de um único item, criar um objeto encapsulando estas informações e inseri-lo na lista de produtos do estoque: hrecuperar um item e inserir na lista de produtosi≡ try { int qtde = Integer.parseInt( analisador.nextToken() ); double preco = Double.parseDouble( analisador.nextToken() ); produtos[n] = new Item( produto, qtde, preco ); n++; } catch( NumberFormatException e ) { System.out.println( "ERRO no arquivo de entrada: linha ignorada" ); System.out.println( linha ); } Após a leitura de todas as linhas do arquivo de estoque, ele é fechado: hfechar o arquivo de estoquei≡ // FECHAR O ARQUIVO DE TEXTO try { filtro.close(); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } c Copyright °1998-2004, Dr. Italo S. Vega 15-13 Laboratório de Programação Maio de 2004 Por último, a lista de produtos é apresentada, contabilizando-se o valor total de cada produto disponível: hmostrar o relatório do estoquei≡ public void gerarRelatorio() { for( int i = 0; i < produtos.length; i++ ) { System.out.println( produtos[i] ); } } A classe Item modela a estrutura de informações de um item estocado. A estrutura é composta pelo nome do produto, sua quantidade em estoque e o seu preço unitário. Adicionalmente, uma propriedade de formatação é utilizada no momento da conversão feita pelo serviço toString(): hlab/estoque/Item.javai≡ import java.text.DecimalFormat; public class Item { private String _produto; private int _quantidade; private double _preco; private DecimalFormat _formato; public Item( String produto, int quantidade, double preco ) { _produto = produto; _quantidade = quantidade; _preco = preco; _formato = new DecimalFormat( "0.##" ); } public String toString() { return _produto + ":" + _quantidade + " a " + _preco + " = " + _formato.format( (_quantidade * _preco) ); } } c Copyright °1998-2004, Dr. Italo S. Vega 15-14 Laboratório de Programação Maio de 2004 E XERCÍCIOS 15.1 M ODELO GERAL DE ESCRITA EM ARQUIVOS DE TEXTO o programa de escrita em arquivos de dados com filtro: Tarefa 15.1.1 Crie o projeto ex15.1. Tarefa 15.1.2 Crie a classe EscritaTextoFiltro. Tarefa 15.1.3 Crie escritor.escrever(). o objeto escritor e 15.2 M ODELO GERAL DE LEITURA DE ARQUIVOS DE TEXTO Implemente e execute envie a mensagem Implemente e execute o programa de leitura em arquivos de dados com filtro: Tarefa 15.2.1 Crie o projeto ex15.2. Tarefa 15.2.2 Crie a classe LeituraTextoFiltro. Tarefa 15.2.3 Crie o objeto leitor e envie a mensagem leitor.ler(). 15.3 C ONTROLE DE E STOQUE Sobre o sistema de controle de estoque: Tarefa 15.3.1 Crie o projeto ex15.3. Tarefa 15.3.2 Implemente o programa do sistema de controle de estoque. Tarefa 15.3.3 Crie os objetos aldo:Estoquista e bazar:Estoque. Tarefa 15.3.4 Qual o efeito do processamento da mensagem aldo.cadastrar()? Tarefa 15.3.5 Qual o efeito do processamento das bazar.recuperarItens() seguida de bazar.gerarRelatorio()? mensagens Tarefa 15.3.6 Altere o arquivo Estoque.java de modo a ser apresentado o valor total estocado (somatória dos valores dos produtos estocados). OBS: procure utilizar um novo método totalizar(produtos):void que recebe o array produtos por referência e retorna o cálculo solicitado. c Copyright °1998-2004, Dr. Italo S. Vega 15-15