EEL170 Computação I 3a Série de slides Versão de 09/10/2015 Função confirmar Em muitos programas temos de confirmar uma opção com S ou N. Vamos fazer então uma função que retorna a escolha realizada pelo usuário. // esta função exige o uso da unit crt Function funConfirmar(mens: string): char; (* Função para confirmar ou não uma opção do usuário; data: ...; Responsável: ... *) Var Carac: char; Begin repeat Writeln(mens,’ Confirme ou não sua opção com S ou N); Carac := upcase(readkey); If (carac <> ‘S’) and (carac <> ‘N’) Then Writeln(‘Opção inválida. Tente novamente’); Until (carac = ‘S’) or (carac = ‘N’); funConfirmar := carac; End; ESTRUTURAS DE DADOS PERSISTENTES ARQUIVOS Arquivos nas linguagens de programação Arquivo: estrutura de dados persistente Arquivos comuns nas linguagens de programação: Binário: acesso sequencial e direto Texto: acesso sequencial e portabilidade Características • E/S: teclado, monitor e impressora podem ser considerados arquivos do tipo texto • Armazenamento persistente de dados: arquivos em meios magnéticos, óticos ou eletrônicos • Arquivo: tipo de variável • Organização: como estão dispostas as unidades de informação no arquivo: linear seqüencial, árvore, ... • Formas de acesso – Seqüencial: – escrita das informações de forma seqüencial uma após a outra – leitura na ordem em que as informações foram escritas – Direto: pode-se ler/escrever em qualquer ordem Associação Há programas, como o editor de textos, em que o usuário pode escolher os nomes (externos) de seus textos. No programa os arquivos tem um identificador (interno) Para cada arquivo utilizado no programa é necessário associar o nome interno ao externo: Antes de se usar, uma variável arquivo deve ser associada a um identificador externo Feita a associação, o arquivo pode ser aberto para ser lido/escrito Arquivo texto O arquivo deve ser do tipo texto Após associar e abrir pode-se escrever/ler A leitura/escrita é sequencial Para escrever: o conteúdo de variáveis são escritas no arquivo através de comandos de e/s da linguagem de programação Para ler: o conteúdo do arquivo é lido para variáveis através de comandos de e/s da linguagem de programação Arquivo binário O arquivo tipado deve ser de um tipo (estruturado) Para ler/escrever sequencialmente: Ler do arquivo para uma variável estruturada com o mesmo tipo do arquivo Escrever no arquivo os dados de uma variável estruturada do mesmo tipo do arquivo Para ler/escrever com acesso direto: Posicionar o arquivo na posição em que se deseja ler/escrever Ler do arquivo para variável do mesmo tipo do arquivo Escrever de variável para o arquivo Ler e escrever em arquivo ler Variável arquivo Variável transferência escrever Arquivo externo eof Ooo 0 1 2 3 4 n Arquivos em Pascal • Texto - <id arquivo>:text – Organização seqüencial de caracteres – Pode ser formatado em linhas terminadas por CR – acesso seqüencial • Tipado - <id arquivo>:file of <tipo do arquivo>; – Arquivo binário – Organização seqüencial dos componentes definidos no <tipo do arquivo> – Estruturado como uma sequência dos componentes nas posições 0, 1, 2, ... – acesso seqüencial ou direto pela posição • Sem tipo definido - <arquivo>:file; – Similar ao tipado, mas seus componentes não tem uma estrutura pré-definida Procedimentos e funções p/ arquivos TEXTO TIPADO SEM TIPO Associar Assign assign assign Abrir Reset append Reset Reset Criar rewrite rewrite rewrite Fechar close Close truncate Close truncate Escrever Write[ln] write blockwrite Ler Read[ln] Read blockread seek seek eof eof filepos filepos Posicionar Teste fim arq Posição atual eof Outros procedimentos e funções • Procedimentos genéricos – – – – – – Chdir(patch): mudar o diretório corrente Erase(arquivo): destruir arquivo Getdir(d,s): retorna o diretório corrente de um disco Mkdir(s): cria um diretório Rename(nomeexterno,novonome): renomear Rmdir(s): remove um diretório vazio • Unit do Pascal com procedimentos e funções – dos Exercícios de arquivos com dados contábeis Dados de cada movimento contábil: Obs.: movimento contábil – movimentação de conta a débito ou a crédito Número da conta: inteiro com 4 dígitos; D/C: um caractere “d” ou “c” para indicar se o movimento é a débito ou a crédito; Valor: um real para o valor do movimento Programas: a) Gravar movimentos contábeis em uma matriz; Ordenar os movimentos por ordem de conta; Gravar um arquivo ordenado com os movimentos; b) Dividir o arquivo obtido em “a” em dois ordenados, um com os débitos, e outro com os créditos; c) Concatenar os dois arquivos obtidos em “b” gerando um arquivo; d) Realizar um merge dos dois arquivos obtidos em “b”. Aplicação • Problema: dar persistência a uma agenda que foi implementada utilizando-se uma matriz, com nome e telefone dos contatos. • Solução: incluir dois procedimentos no programa com acessos sequenciais: – Abrir: no início do programa para abrir o arquivo e copiar os dados do arquivo para a matriz – Salvar: no fim do programa para salvar os dados da matriz no arquivo Aplicação • Problema: Uma pequena instituição comercial deseja o controle do estoque dos produtos que comercializa. Para cada produto manter: – – – – Código: identifica o produto; Nome: descrição sintética do produto Preço: valor de venda; Quantidade: quantidade em estoque • O sistema deve permitir – incluir um produto novo – o código é dado pelo sistema em ordem seqüencial crescente – Comprar um produto já existente (entrada de uma ou mais unidades) – Vender um produto (uma ou mais unidades) – Consultar um produto pelo código – Listar todos os produtos de 20 em 20 – Alterar dados do produto (não pode alterar o código) • Todas as operações são realizadas com acesso direto pelo código Observação Como o sistema poderá produzir o código do produto, para facilitar vamos produzir este código de forma sequencial a partir do zero. Aplicação • Para o problema anterior: – O usuário escolhe o código do produto, que pode variar de 0 a 3.000. – O usuário pode excluir um produto desde que a quantidade do mesmo seja igual a zero. – Todas as operações com acesso direto pelo código a menos da listagem que será sequencial. Observações - Como o maior código é 3.000, ele poderá continuar a ser a posição da informação do produto no arquivo, pois o arquivo não será grande. - Ao criar um produto verificar se o código já existe. - Pode-se posicionar além do fim do arquivo para incluir um produto novo. Aplicação • Para o mesmo problema anterior: – O usuário escolhe o código do produto, e este código tem 9 (nove) dígitos numéricos, apesar de existirem apenas 3.000 produtos – O usuário pode excluir um produto desde que a quantidade do mesmo seja igual a zero. – Todas as operações com acesso direto pelo código a menos da listagem que será sequencial. Observação Como o código numérico tem nove dígitos, ele não poderá ser a posição das informações no arquivo, pois exigiria um arquivo com o tamanho de 1 trilhão, para apenas 3.000 registros com informações. Fazer um hash para obter a posição no arquivo a partir do código, tratando as colisões (sinônimos). O hash poderá ser o resto da divisão do código por 6.001. Abrir ou criar arquivo Quando se trabalha com um arquivo é necessário abri-lo para ler (arquivo já existente) ou escrever (arquivo já existente ou ainda não). Na maior parte dos casos de programas com arquivos, o usuário deverá informar o identificador externo do arquivo. Como essas operações podem seguir um padrão comum, será conveniente fazer um procedimento a ser incluído em uma biblioteca. Como há diferenças nos arquivos texto e binários, será necessário fazer procedimentos distintos para os arquivos texto e binários Passos gerais para abrir/criar Solicitar ao usuário que informe o nome que deseja dar ao arquivo, alertando para os dados que o arquivo conterá O programador seleciona a operação abrir ou criar novo arquivo O procedimento verifica se não houve erro: No caso de “abrir” o arquivo deve existir, caso contrário há erro e o procedimento deve ser reiniciado; No caso de “criar novo” o arquivo não pode existir; se existir pode haver erro e deve ser solicitado ao usuário que decida se o arquivo deve ser realmente criado, destruindo as informações existentes, ou então não deve ser criado, e neste caso o procedimento deve ser reiniciado para novas opções do usuário. Procedimento - arquivo binário procedure abrirArqBin(arqMens: string; operac: char; arq: file; var nomeExt: string); (* Procedimento padrão para abrir arquivo existente ou criar arquivo novo. Parâmetros: mensagem para o usuário; operação a ou c para abrir ou criar; arquivo não tipado; nome externo escolhido pelo usuário *) // Uses digDados, dos, crt; // bibliotecas var Correto: boolean; // variável para controlar erro Resultado: integer; // resultado da busca do arquivo begin repeat correto := true; // inicia sem erro writeln(arqMens); digStr(‘Informar o nome completo do arquivo’, 40, 6, nomeExt); assign(arq, nomeExt); {$i-} // para inibir rotina de tratamento de erro reset(arq); // tenta abrir para verificar se já existe {$I+} // reativar tratamento de erro resultado := ioresult; If (operac = ‘A’) and (resultado <> 0) then begin writeln(‘Erro: o arquivo não existe. Escolha outro nome’); Correto := false; end; If (operac = ‘C’) and (resultado = 0) then begin If funConfirmar (‘Atenção: o arquivo já existe!!! Deve confirmar se deve mesmo ser destruido e criado um novo ou não’) = ‘N’ then correto := false; until correto; if operac = ‘C’ then rewrite(arq); end; Procedimento - arquivo texto Procedure abrirArqText(arqMens: string; operac: char; arq: text; var nomeExt: string); (* Procedimento padrão para abrir arquivo existente ou criar arquivo novo. Parâmetros: mensagem para o usuário; operação a ou c para abrir ou criar; arquivo texto; nome externo escolhido pelo usuário *) // Uses digDados, dos, crt; // bibliotecas Var Correto: boolean; // variável para controlar erro Resultado: integer; // resultado da busca do arquivo begin repeat correto := true; // inicia sem erro writeln(arqMens); digStr(‘Informar o nome completo do arquivo’, 40, 6, nomeExt); assign(arq, nomeExt); {$i-} // para inibir rotina de tratamento de erro reset(arq); // tenta abrir para verificar se já existe {$I+} resultado := ioresult; If (operac = ‘A’) and (resultado <> 0) then begin writeln(‘Erro: o arquivo não existe. Escolha outro nome’); Correto := false; end; If (operac = ‘C’) and (resultado = 0) then begin If funConfirmar (‘Atenção: o arquivo já existe!!! Deve confirmar se deve mesmo ser destruido e criado um novo ou não’) = ‘N’ then correto := false; until correto; if operac = ‘C’ then rewrite(arq); end; Acesso binário • Uma estrutura linear ordenada segundo um item de seus registros, permite uma busca por acesso binário por valores desse item – “Chave de ordenação e de acesso” – Nessa busca a cada acesso despreza-se metade de seu espaço de busca – A quantidade de acessos será proporcional ao logaritmo na base 2 da quantidade de registros da estrutura • Um arquivo binário ordenado segundo um dos itens de seu registro – sua”chave” de ordenação – pode ser acessado via busca binária sobre este item Exemplo – Uma biblioteca com os livros dispostos no cadastro em ordem de número crescente de ficha catalográfica. O número da ficha é a chave do arquivo – Para acessar: calcular o meio do arquivo (em tamanho) e comparar a chave do livro que se busca com a chave do meio do arquivo. Se a chave que se busca é igual à chave do meio, o livro foi encontrado. Se a chave buscada é maior do que a chave do meio, o livro buscado está na metade superior do arquivo, senão está na parte inferior. Toma-se a metade restante e calcula-se seu meio, e se continua o processo até encontrar a chave ou verificar que a chave não existe. Exemplo Numérico - 1 Fichas: 21 34 54 65 70 74 85 90 95 • Procurar a ficha 54 • 54 -----------------------| meio • Resta 21 34 54 65 • 54 -----------| meio • Resta 54 65 • 54-------| meio • Encontrou Exemplo Numérico - 2 • Fichas: 21 34 54 65 70 74 85 90 95 • Procurar a ficha 88 • 88 ----------------------------| meio • Resta 74 85 90 95 • 88 -----------| meio • Resta 90 95 • 88-------|meio • Resta 95 • Não encontrou Algoritmo busca binária • Algoritmo buscaBinArquivo • (* algoritmo para a busca binária em um arquivo binário ordenado pelo item “ficha” de seu registro; Responsável... Data ... *) • Tipo tReg: registro ficha: inteiro • ........ • Fim • Var biblioteca: file of tReg • Livro: tReg • Inicio • ...... • Topo ← tamanho (arquivo) • Base ← 1 • Ler fichaBusca // chave de busca • Centro ← (topo + base) div 2 // truncar • Posicionar arquivo biblioteca em centro • Leia (biblioteca, livro) • Enquanto (base <= topo) e (livro.ficha <> fichaBusca) faça – Inicio – Se fichaBusca > livro.ficha – Então base ← centro +1 – Senão topo ← centro -1 – Centro ← (topo + base) div 2 // truncar – Posicionar arquivo biblioteca em centro – Leia (biblioteca, livro) – Fim • Se fichaBusca = livro.ficha • Então escreva (“Encontrou”, livro.ficha, …) • Senão escreva (“Livro não encontrado”) • fim