Pontifícia Universidade Católica de São Paulo Departamento de Ciência da Computação Apontamento 16 Maio de 2004 LP: Laboratório de Programação Prof. ISVega Arquivos Binários CONTEÚDO 16.1 Processamento de Arquivos de Dados . . . . . 16.2 Processamento de Bytestreams . . . . . . . . 16.2.1Escrita COM Filtros . . . . . . . . . . . 16.3 Aplicação: Controle de Estoque . . . . . . . . 16.3.1Criação do Arquivo de Estoque . . . . . 16.3.2Processamento do Arquivo de Estoque Exercícios . . . . . . . . . . . . . . . . . . . . . . . . 16.4 Anexo: Listagem do Protótipo . . . . . . . . . 16.4.1Pacote Principal . . . . . . . . . . . . . 16.4.2Pacote dados . . . . . . . . . . . . . . . 16.4.3Pacote estoque2 . . . . . . . . . . . . . Objetivos . . . . . . . . . . . . . . . . . . . . . . 1 2 2 6 6 8 12 14 14 14 15 • Mostrar como ler e escrever arquivos binários em Java. 16.1 Processamento de Arquivos de Dados Informações são armazenadas em um arquivo ou na forma de bytes (dados) ou na forma de texto (linhas de caracteres). Assim, as seqüências de bytes podem ser 1 Laboratório de Programação Maio de 2004 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. 16.2 Processamento de Bytestreams Processamento de Dados COM Filtros O processamento de arquivos de dados (binários) com filtros (precisa indicar um FileInputStream ou FileOutputStream como origem do texto) é feito por instâncias da classe DataInputStream e DataOutputStream. 16.2.1 Escrita COM Filtros A escrita de dados sem filtros é feita por instâncias da classe DataOutputStream, segundo o padrão: hlab/padrao/EscritaDadosFiltro.javai≡ hEscrita dados: dependênciasi public class EscritaDadosFiltro { public escrever() { hEscrita dados: criar arquivo de dadosi hEscrita dados: escrever dadosi hEscrita dados: fechar arquivo de dadosi } } Dependências Externas O método de escrita em arquivos de dados depende das seguintes classes da biblioteca Java: hEscrita dados: dependênciasi≡ import java.io.FileOutputStream; import java.io.DataOutputStream; import java.io.FileNotFoundException; import java.io.IOException; Método de Escrita O primeiro passo envolve a criação do arquivo e do filtro de escrita de dados: c Copyright °1998-2004, Dr. Italo S. Vega 16-2 Laboratório de Programação Maio de 2004 hEscrita dados: criar arquivo de dadosi≡ // ABERTURA DataOutputStream filtro = null; try { FileOutputStream arquivo = new FileOutputStream( "dados.dat" ); filtro = new DataOutputStream( arquivo ); } catch( FileNotFoundException e ) { System.out.println( "Falha na criação do arquivo" ); } Em seguida, escrevem-se os dados em si: hEscrita dados: escrever dadosi≡ // ESCRITA try { filtro.writeChar( ’a’ ); } catch( IOException e ) { System.out.println( "Falha na escrita" ); } Por último, fecha-se o arquivo de dados: hEscrita dados: fechar arquivo de dadosi≡ // FECHAMENTO try { filtro.close(); } catch( IOException e ) { System.out.println( "Falha ao fechar" ); } Serviços de Escrita Os serviços oferecidos pelo filtro de escrita são: c Copyright °1998-2004, Dr. Italo S. Vega 16-3 Laboratório de Programação Maio de 2004 flush close size write (byte) writeBoolean writeByte writeBytes writeChar writeChars (unicode strings) writeDouble writeFloat writeInt writeLong writeShort writeUTF Leitura COM Filtros A leitura de dados com filtros é feita por instâncias da classe DataInputStream. hlab/padrao/LeituraDadosFiltro.javai≡ hLeitura dados: dependênciasi public class LeituraDadosFiltro { public ler() { hLeitura dados: abrir e criar o filtroi hLeitura dados: ler os dadosi hLeitura dados: fechar o arquivoi } } Dependências Externas O método de escrita em arquivos de dados depende das seguintes classes da biblioteca Java: hLeitura dados: dependênciasi≡ import java.io.FileInputStream; import java.io.DataInputStream; import java.io.FileNotFoundException; import java.io.EOFException; import java.io.IOException; Método de Leitura O primeiro passo envolve a abertura do arquivo a ser lido e do filtro de leitura de dados: c Copyright °1998-2004, Dr. Italo S. Vega 16-4 Laboratório de Programação Maio de 2004 hLeitura dados: abrir e criar o filtroi≡ // ABERTURA DataInputStream filtro = null; try { FileInputStream arquivo = new FileInputStream( "dados.dat" ); filtro = new DataInputStream( arquivo ); } catch( FileNotFoundException e ) { System.out.println( "Arquivo inexistente" ); } Em seguida, lêem-se os dados em si: hLeitura dados: ler os dadosi≡ // LEITURA try { System.out.println( filtro.readChar() ); } catch( EOFException e ) { System.out.println( "Fim do arquivo" ); } catch( IOException e ) { System.out.println( "Falha na leitura" ); } Por último, fecha-se o arquivo de dados: hLeitura dados: fechar o arquivoi≡ // FECHAMENTO try { filtro.close(); } catch( IOException e ) { System.out.println( "Falha ao fechar"); } Serviços de Leitura Os serviços oferecidos pelo filtro de leitura são: c Copyright °1998-2004, Dr. Italo S. Vega 16-5 Laboratório de Programação Maio de 2004 close read (byte) readBoolean readByte readChar readDouble readFloat readFully readInt readLong readShort readUnsignedByte readUnsignedShort readUTF (strings) skipBytes 16.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 arquivo, nesta aplicação, é do tipo arquivo de dados. 16.3.1 Criação do Arquivo de Estoque A criação do arquivo de estoque, denominado estoque.dat, segue o padrão anteriormente descrito para o caso de escrita arquivos de dados. hlab/estoque2/Estoquista.javai≡ hpacotes importados na criação do estoquei public class Estoquista { DataOutputStream filtro = null; public void cadastrar() { habrir o arquivo de dados 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 dadosi System.exit(0); } } Dependências Externas As seguintes classes são necessárias para criar o arquivo de estoque de produtos: c Copyright °1998-2004, Dr. Italo S. Vega 16-6 Laboratório de Programação Maio de 2004 hpacotes importados na criação do estoquei≡ import java.io.FileOutputStream; import java.io.DataOutputStream; import java.io.IOException; Método de Criação do Estoque A abertura e criação do filtro de escrita obedece ao padrão: habrir o arquivo de dados e criar o filtro de escrita do estoquei≡ // ABRIR ARQUIVO DE DADOS E CRIAR FILTRO try { FileOutputStream arqDados = new FileOutputStream( "estoque.dat" ); filtro = new DataOutputStream( arqDados ); } 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 leitor optico para a leitura das quantidades. Uma repetição controlada por contador percorre cada produto e solicita a quantidade existente. Este valor é convertido para int e posteriormente escrito no arquivo de dados. Um aspecto importante nesta parte do método de escrita é que a quantidade de caracteres que compõe o nome do produto precede este nome no arquivo: hobter as quantidades de produtos e escrever no arquivoi≡ // ESCREVER NO ARQUIVO DE DADOS try { String[] produtos = { "Lapis", "Borracha", "Caderno", "Caneta" }; double[] 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] + "?"); filtro.writeInt( produtos[i].length() ); // <- tamanho do string filtro.writeChars( produtos[i] ); filtro.writeInt( qtde ); filtro.writeDouble( precos[i] ); } optico = null; } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } c Copyright °1998-2004, Dr. Italo S. Vega 16-7 Laboratório de Programação Maio de 2004 A parte final esvazia o filtro e fecha o arquivo de dados contendo as informações sobre o estoque atual: hesvaziar o filtro e fechar o arquivo de dadosi≡ // ESVAZIAR FILTRO E FECHAR O ARQUIVO DE DADOS 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/estoque2/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; } } 16.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/estoque2/Estoque.javai≡ hpacotes importados no processamento do estoquei public class Estoque { DataInputStream filtro = null; public void recuperarItens() { habrir o arquivo de dados 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 } c Copyright °1998-2004, Dr. Italo S. Vega 16-8 Laboratório de Programação Maio de 2004 São os seguintes os arquivos importados para o processamento do estoque: hpacotes importados no processamento do estoquei≡ import java.io.FileInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.FileNotFoundException; import java.io.EOFException; A abertura do arquivo de estoque para leitura, segue o padrão normal visto anteriormente: habrir o arquivo de dados e criar o filtro de leitura do estoquei≡ // ABRIR ARQUIVO DE DADOS E CRIAR FILTRO try { FileInputStream arqDados = new FileInputStream( "estoque.dat" ); filtro = new DataInputStream( arqDados ); } catch( FileNotFoundException e ) { System.out.println( e ); System.exit( 1 ); } A carga de informações do arquivo faz uso do filtro de leitura de dados. O nome do produto, sua quantidade e o preço unitário são carregados diretamente do arquivo. A partir deles, um novo contexto da classe Item é construído, sendo de imediato inserido na lista produtos, representando o estoque atual: hcarregar as informações sobre cada produto e gerar o relatórioi≡ // LER DO ARQUIVO DE DADOS Item[] produtos = new Item[ 100 ]; int n = 0; try { hcarregar todos os itens do arquivoi } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } A carga de todos os itens do arquivos é feita com uma estrutura de controle de repetição com parada por sentinela: c Copyright °1998-2004, Dr. Italo S. Vega 16-9 Laboratório de Programação Maio de 2004 hcarregar todos os itens do arquivoi≡ boolean acabou = false; while( ! acabou ) { try { hcarregar um item do arquivo de estoquei n++; } catch( NumberFormatException e ) { System.out.println( "ERRO no arquivo de entrada: dados ignorados" ); } catch( EOFException e ) { acabou = true; } } A carga individual de um item recupera cada uma das suas partes: hcarregar um item do arquivo de estoquei≡ String produto = ""; int k = filtro.readInt(); // <- tamanho do nome do produto for( int i = 0; i < k; i++ ) { produto += filtro.readChar(); } int qtde = filtro.readInt(); double preco = filtro.readDouble(); produtos[n] = new Item( produto, qtde, preco ); Após a leitura de todos os dados do arquivo de estoque, ele é fechado: hfechar o arquivo de estoquei≡ // FECHAR O ARQUIVO DE DADOS try { filtro.close(); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } 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] ); } } c Copyright °1998-2004, Dr. Italo S. Vega 16-10 Laboratório de Programação Maio de 2004 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/estoque2/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 16-11 Laboratório de Programação Maio de 2004 E XERCÍCIOS 16.1 P ROCESSO DE E SCRITA DE D ADOS Implemente e execute o programa de escrita em arquivos de dados com filtro (EscritaDadosFiltro.java na seção de listagens). Tarefa 16.1.1 Crie o projeto ex16.1. Tarefa 16.1.2 Implemente de acordo com o texto. 16.2 P ROCESSO L EITURA DE D ADOS Implemente e execute o programa de escrita em arquivos de dados com filtro (LeituraDadosFiltro.java na seção de listagens). DE Tarefa 16.2.1 Crie o projeto ex16.2. Tarefa 16.2.2 Implemente de acordo com o texto. 16.3 C ONTROLE DE E STOQUE Sobre o sistema de controle de estoque: Tarefa 16.3.1 Crie o projeto ex16.3. Tarefa 16.3.2 Implemente o programa do sistema de controle de estoque. Tarefa 16.3.3 Crie os objetos aldo:Estoquista e bazar:Estoque. Tarefa 16.3.4 Qual o efeito do processamento da mensagem aldo.cadastrar()? Tarefa 16.3.5 Qual o efeito do processamento das bazar.recuperarItens() seguida de bazar.gerarRelatorio()? mensagens Tarefa 16.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. 16.4 C ÁLCULO M ÉDIA Crie uma aplicação que calcula o mostra as notas e médias finais de uma turma de alunos. Tarefa 16.4.1 DE Crie o projeto ex16.4. Tarefa 16.4.2 Crie a classe Aluno.java descrevendo um aluno com as seguintes informações: primeiro nome, número de inscrição, notas das provas P1, P2 e PS. Por exemplo: Carlos 112233 5.5 7.8 Andrea 333222 4.6 2.3 8.8 Zizi 545454 3.8 7.7 (Estas informações devem estar armazenadas no arquivo de dados alunos.txt) c Copyright °1998-2004, Dr. Italo S. Vega 16-12 Laboratório de Programação Maio de 2004 Tarefa 16.4.3 Crie a classe Turma.java, que lê o arquivo alunos.txt e mostra todas as suas linhas na tela. Tarefa 16.4.4 Acrescente um método na classe Turma.java, de modo que ele armazene o conteúdo do arquivo alunos.txt em um arquivo de dados denominado alunos.dat. Quais os tamanhos destes arquivos? Tarefa 16.4.5 Acrescente a classe Disciplina.java, de forma que seja carregado o arquivo de dados alunos.dat e sejam apresentadas todas as informações sobre um aluno, inclusive a média final de cada um deles. A média final é calculada da seguinte forma: mf = P1 + 2 × P2 2 caso o aluno não tenha feito PS . Se o aluno fez PS , esta nota deverá substituir a menor das notas P 1 ou P 2: mf = PS + 2 × P 2 2 mf = P 1 + 2 × PS 2 se P 1 < P 2; ou se P 2 < P 1. c Copyright °1998-2004, Dr. Italo S. Vega 16-13 Laboratório de Programação Maio de 2004 16.4 Anexo: Listagem do Protótipo 16.4.1 Pacote Principal 16.4.2 Pacote dados Classe EscritaDadosFiltro.java import java.io.FileOutputStream; import java.io.DataOutputStream; import java.io.FileNotFoundException; import java.io.IOException; public class EscritaDadosFiltro { public static void main( String[] args ) { // ABERTURA DataOutputStream filtro = null; try { FileOutputStream arquivo = new FileOutputStream( "dados.dat" ); filtro = new DataOutputStream( arquivo ); } catch( FileNotFoundException e ) { System.out.println( "Inexistente" ); } // ESCRITA try { filtro.writeChar( ’a’ ); } catch( IOException e ) { System.out.println( "Falha na escrita" ); } // FECHAMENTO try { filtro.close(); } catch( IOException e ) { System.out.println( "Falha ao fechar" ); } } } Classe LeituraDadosFiltro.java import java.io.FileInputStream; import java.io.DataInputStream; import java.io.FileNotFoundException; import java.io.EOFException; import java.io.IOException; public class LeituraDadosFiltro { public static void main( String[] args ) { // ABERTURA DataInputStream filtro = null; try { FileInputStream arquivo = new FileInputStream( "dados.dat" ); filtro = new DataInputStream( arquivo ); } catch( FileNotFoundException e ) { System.out.println( "Inexistente" ); } // LEITURA try { System.out.println( filtro.readChar() ); } catch( EOFException e ) { System.out.println( "Fim do arquivo" ); } catch( IOException e ) { System.out.println( "Falha na leitura" ); c Copyright °1998-2004, Dr. Italo S. Vega 16-14 Laboratório de Programação Maio de 2004 } // FECHAMENTO try { filtro.close(); } catch( IOException e ) { System.out.println( "Falha ao fechar"); } } } 16.4.3 Pacote estoque2 Classe Estoque.java import java.io.FileInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.FileNotFoundException; import java.io.EOFException; public class Estoque { DataInputStream filtro = null; public void recuperarItens() { // ABRIR ARQUIVO DE DADOS E CRIAR FILTRO try { FileInputStream arqDados = new FileInputStream( "estoque.dat" ); filtro = new DataInputStream( arqDados ); } catch( FileNotFoundException e ) { System.out.println( e ); System.exit( 1 ); } // LER DO ARQUIVO DE DADOS Item[] produtos = new Item[ 100 ]; int n = 0; try { boolean acabou = false; while( ! acabou ) { try { String produto = ""; int k = filtro.readInt(); // <-- tamanho do nome do produto for( int i = 0; i < k; i++ ) { produto += filtro.readChar(); } int qtde = filtro.readInt(); double preco = filtro.readDouble(); produtos[n] = new Item( produto, qtde, preco ); n++; } catch( NumberFormatException e ) { System.out.println( "ERRO no arquivo de entrada: dados ignorados" ); } catch( EOFException e ) { acabou = true; } } } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } // FECHAR O ARQUIVO DE DADOS try { filtro.close(); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); c Copyright °1998-2004, Dr. Italo S. Vega 16-15 Laboratório de Programação Maio de 2004 } } public void gerarRelatorio() { for( int i = 0; i < produtos.length; i++ ) { System.out.println( produtos[i] ); } } } Classe Estoquista.java import java.io.FileOutputStream; import java.io.DataOutputStream; import java.io.IOException; public class Estoquista { DataOutputStream filtro = null; public void cadastrar() { // ABRIR ARQUIVO DE DADOS E CRIAR FILTRO try { FileOutputStream arqDados = new FileOutputStream( "estoque.dat" ); filtro = new DataOutputStream( arqDados ); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } // ESCREVER NO ARQUIVO DE DADOS try { String[] produtos = { "Lapis", "Borracha", "Caderno", "Caneta" }; double[] 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] + "?"); filtro.writeInt( produtos[i].length() ); // <-- tamanho do string filtro.writeChars( produtos[i] ); filtro.writeInt( qtde ); filtro.writeDouble( precos[i] ); } optico = null; } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } // ESVAZIAR FILTRO E FECHAR O ARQUIVO DE DADOS try { filtro.flush(); filtro.close(); } catch( IOException e ) { System.out.println( e ); System.exit( 1 ); } System.exit(0); } } Classe Item.java 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.##" ); c Copyright °1998-2004, Dr. Italo S. Vega 16-16 Laboratório de Programação Maio de 2004 } public String toString() { return _produto + ":" + _quantidade + " a " + _preco + " = " + _formato.format( (_quantidade * _preco) ); } } @%%% FIGURA --%\figuraDirPP[0.6]{\rota/\diretorio/images} %{\dirFig-f-12} %{Esquema de um tanque de água.} %%% --- FIGURA %\begin{isvSSlide} %\isvExercitar{5} %%\isvDiscutir{1} %\end{isvSSlide} Classe LeitorOptico.java 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; } } c Copyright °1998-2004, Dr. Italo S. Vega 16-17