Baixe o app para aproveitar ainda mais
Prévia do material em texto
Manipulação de Arquivos Universidade Federal de Lavras Prof. Joaquim Uchôa Profa. Juliana Greghi Profa. Marluce Pereira Roteiro ● Arquivos e streams● Arquivos binários e arquivos tipados 2 Streams e arquivos 3 Introdução - i O armazenamento de dados em variáveis é temporário, isto é, os dados se perdem quando o programa termina sua execução. Para manter grandes quantidades de dados de forma permanente são usados arquivos. Arquivos são armazenados em dispositivos de memória secundária, tais como: discos rígidos, CD, DVD, pendrives, cartões de memória, etc. 4 Introdução - ii Dispositivos de memória secundária podem reter grandes volumes de dados por longos períodos de tempo. Processos de entrada e saída de dados em arquivos são executados por funções especialmente criadas para esta finalidade e disponibilizadas em bibliotecas específicas. 5 Streams ● Stream é uma abstração que representa um dispositivo onde as operações de entrada e saída são realizadas ● Streams são associados a uma fonte ou destino físico de caracteres como um arquivo em disco, teclado ou console (tela do computador) ● Em C++ há classes (bibliotecas) específicas para tratar streams 6 Streams em C++ 7 Operações comuns em arquivos ● Abertura e fechamento de arquivos; ● Apagar um arquivo; ● Leitura e escrita; ● Indicação de que o fim de arquivo foi atingido; ● Posicionar o arquivo em um ponto determinado. Nem sempre essas ações são fornecidas na mesma biblioteca em uma dada linguagem de programação. 8 Arquivos - importante ➔ Para escrever ou ler, é necessário abrir o arquivo. ➔ Ao final das operações necessárias o programa deve fechar o arquivo. ➔ Quando um arquivo é fechado, o conteúdo dos buffers é descarregado para o dispositivo externo. 9 Arquivos e streams Dados em arquivos podem ser manipulados em dois diferentes tipos de fluxos de dados (stream): ● Fluxo de texto: composto por uma sequência de bytes que denotam uma sequência de caracteres. Para isso, utiliza-se geralmente caracteres da tabela ASCII ou UTF-8. ● Fluxo binário: composto por uma sequência de bytes lidos, sem tradução, diretamente do dispositivo externo. Os dados são gravados exatamente como estão organizados na memória do computador. Dados estão sujeitos às convenções dos programas que os geraram. 10 Streams Em C++, arquivos são entendidos como streams cujos dados estão guardados em um dispositivo de armazenamento secundário. Existem três tipos de streams para manipulação de arquivos: – ifstream (para entrada/leitura de dados) //i=input – ofstream (para saída/escrita de dados) //o=output – fstream (para entrada e saída) Para ter acesso, deve-se incluir a biblioteca <fstream>. 11 Manipulando arquivos com streams Sobre a escrita (ofstream): ➔ Se o arquivo não existir, o mesmo será criado; ➔ Se o arquivo já existir, seu conteúdo será apagado. Sobre a leitura (ifstream): ➔ Se o arquivo não existir, o mesmo não será criado. A forma mais simples de utilizar arquivos é como um fluxo de texto: ➔ Escrevemos no arquivo de modo similar ao uso de cout; ➔ Lemos no arquivo de modo similar ao uso de cin 12 Arquivo de saída: escrevendo no arquivo ➔ Declaração: ofstream arquivo; ➔ Abrir: arquivo.open(“meusdados.txt”); ➔ Fechar: desconectar a stream do arquivo “meusdados.txt” arquivo.close(); ➔ Operador de inserção <<: comportamento idêntico ao cout arquivo << “Gravando no arquivo”; 13 Escrevendo no arquivo 14 #include <fstream> using namespace std; int main(){ ofstream arquivo; arquivo.open("meus_dados.txt"); arquivo << "Escrevendo no arquivo..." << endl; arquivo.close(); return 0; } Escrevendo no arquivo 15 #include <fstream> using namespace std; int main(){ ofstream arquivo; arquivo.open("meus_dados.txt"); arquivo << "Escrevendo no arquivo..." << endl; arquivo.close(); return 0; } open=abrir A variável arquivo, do tipo ofstream, é associada ao arquivo meus_dados.txt. Escrevendo no arquivo 16 #include <fstream> using namespace std; int main(){ ofstream arquivo arquivo.open("meus_dados.txt"); arquivo << "Escrevendo no arquivo..." << endl; arquivo.close(); return 0; } close = fechar close() é chamado automaticamente caso a função termine normalmente, sem uso de exit(). Escrevendo no arquivo 17 #include <fstream> using namespace std; int main(){ ofstream arquivo; arquivo.open("meus_dados.txt"); arquivo << "Escrevendo no arquivo..." << endl; arquivo.close(); return 0; } Os dados “saem” do programa e são escritos no arquivo, por isso é semelhante ao cout. Abertura de arquivos O trecho ofstream arquivo; arquivo.open("meus_dados.txt"); é equivalente a: ofstream arquivo("meus_dados.txt"); A segunda opção é preferida, sempre que possível, uma vez que alguns compiladores otimizam o código. 18 ! Assegurando abertura Na prática, para garantir que o arquivo foi criado corretamente, poderíamos fazer: if (arquivo) { /* Bloco de instruções 1 Arquivo apto para uso */ } else { /* Bloco de instruções 2 Arquivo inapto para uso */ } 19 Arquivo de entrada: lendo de um arquivo existente ➔ Declaração: ifstream arquivo; ➔ Abrir: arquivo.open(“meusdados.txt”); ➔ Fechar: desconectar a stream do arquivo “meusdados.txt” arquivo.close(); ➔ Operador de extração >>: comportamento idêntico ao cin string dados; arquivo >> dados; 20 Arquivo de entrada: lendo de um arquivo existente OBSERVAÇÃO: O operador de extração de fluxo (>>) pula os caracteres de espaço em branco, tabulações e nova linha no fluxo de entrada 21 Lendo do arquivo 22 #include <fstream> using namespace std; int main(){ ifstream arquivo; arquivo.open("meus_dados.txt"); string dados; if (arquivo) { while ( arquivo >> dados ) cout << dados << endl; arquivo.close(); } return 0; } Lendo do arquivo 23 #include <fstream> using namespace std; int main(){ ifstream arquivo; arquivo.open("meus_dados.txt"); string dados; if (arquivo) { while ( arquivo >> dados ) cout << dados << endl; arquivo.close(); } return 0; } Esse while será repetido enquanto for possível ler um valor para a variável dados a partir do arquivo Testando a leitura - i O teste while ( arquivo >> dados ) { (...) verifica se foi possível ler do arquivo, verificando, entre outros, se não chegou ao final do arquivo. Esse teste é mais efetivo e eficiente que: while ( !arquivo.eof() ) { arquivo >> dados; (...) 24 Testando a leitura - ii Da mesma forma que o operador de leitura, outros comandos de leitura, como getline() ou get(), podem ser usados como teste para indicar se a última leitura foi realizada ou não com sucesso. Assim, é possível ler dado de um arquivo até que eles acabem. A variável que representa o arquivo também pode ser testada com finalidades similares. 25 Exemplo de uso de arquivos Calcular a média de um conjunto de valores decimais disponibilizados em um arquivo texto chamado “dados.dat”. Não se sabe, a priori, qual a quantidade de números no arquivo, deve-se efetuar a leitura até o último número. 26 27 #include <fstream> #include <iostream> using namespace std; int main(){ ifstream arq("dados.dat"); float soma = 0.0; int num = 0; float valor; if (arq) { while ( arq >> valor ) { soma += valor; num++; } cout << soma/num << endl; } else cout << "arquivo não pode ser aberto!" << endl; return 0; } 28 #include <fstream> #include <iostream> using namespace std; int main(){ ifstream arq("dados.dat"); float soma = 0.0; int num = 0; float valor; if (arq) { while ( arq >> valor ) { soma += valor; num++; } cout << soma/num << endl; } else cout << "arquivo não pode ser aberto!" << endl; return 0; } 29 #include <fstream> #include <iostream> using namespace std; int main(){ ifstream arq("dados.dat");float soma = 0.0; int num = 0; float valor; if (arq) { while ( arq >> valor ) { soma += valor; num++; } cout << soma/num << endl; } else cout << "arquivo não pode ser aberto!" << endl; return 0; } Status de um arquivo Além do controle de operações de Entrada e Saída (E/S), um arquivo também tem informações a respeito do seu status. Quando um operação de E/S falha, o arquivo fica marcado como impróprio para E/S e todas as operações de E/S seguintes falham. Para continuar realizando operações de E/S em arquivo após um erro, será necessário resetar o status e o controle das posições, utilizando arquivo.clear(). 30 Modos de abertura de arquivos Um arquivo pode ser aberto com diferentes configurações. Em C++, os seguintes modos estão disponíveis, além de outros: ❏ ios::app ⇒ Não trunca o arquivo, só permite escrever no fim (app =append ) ❏ ios::ate ⇒ Abre o arquivo, posiciona a cabeça de leitura/escrita na posição final. Permite mover a cabeça de leitura/escrita (ate = at end) ❏ ios::binary ⇒ Abre o arquivo em modo binário (indica apenas se utiliza \n ou \r\n em final de linha) ❏ ios::in ⇒ Abre para leitura. ❏ ios::out ⇒ Abre para escrita (qualquer dado existente no arquivo é apagado e substituído pelos dados escritos no stream) ❏ ios::trunc ⇒ Trunca (abre e apaga o conteúdo) o arquivo. 31 Modos de abertura de arquivos Um arquivo pode ser aberto com diferentes configurações. Em C++, os seguintes modos estão disponíveis, além de outros: ❏ ios::app ⇒ Não trunca o arquivo, só permite escrever no fim (app =append ) ❏ ios::ate ⇒ Abre o arquivo, posiciona a cabeça de leitura/escrita na posição final. Permite mover a cabeça de leitura/escrita (ate = at end) ❏ ios::binary ⇒ Abre o arquivo em modo binário (indica apenas se utiliza \n ou \r\n em final de linha) ❏ ios::in ⇒ Abre para leitura. ❏ ios::out ⇒ Abre para escrita (qualquer dado existente no arquivo é apagado e substituído pelos dados escritos no stream) ❏ ios::trunc ⇒ Trunca (abre e apaga o conteúdo) o arquivo. 32 Para maioria das necessidades, não é necessário o uso desse tipo de recurso. Exemplos ➔ Abertura de arquivo para escrita no fim: ofstream arquivo("meus_dados.txt", ios::app) ➔ Abertura de arquivo em modo binário (forma de escrita do caracter de término de linha- se ‘\n’ ou ‘\r\n’): ofstream arquivo("meus_dados.dat", ios::binary) 33 Exemplos ➔ Abertura de arquivo para escrita no fim: ofstream arquivo("meus_dados.txt", ios::app) ➔ Abertura de arquivo em modo binário (forma de escrita do caractere de término de linha - se ‘\n’ ou ‘\r\n’): ofstream arquivo("meus_dados.dat", ios::binary) 34 Não confundir com abertura de arquivos binários (não é necessário mudar o modo de abertura para isso!). Observações A) ifstream e ofstream já implicam na abertura do arquivo para leitura e escrita, respectivamente, e não precisam ter o modo de abertura especificado. Portanto, estão corretos: ❖ Entrada: ifstream(“arquivo.txt”), ifstream(“arquivo.txt”,ios::in) e fstream(“arquivo.txt”, ios::in | ios::out) ❖ Saída: ofstream(“arquivo.txt”), ofstream(“arquivo.txt”,ios::out) e fstream(“arquivo.txt”, ios::in | ios::out) | significa OU 35 Arquivos binários e arquivo tipados 36 Arquivos binários - i 37 Em computação, são comuns os usos de arquivos texto, mas geralmente são mais usados os arquivos binários. Arquivo binário é qualquer arquivo que contenha uma sequência de bytes que não representem texto puro, podendo armazenar informação adicional sobre o conteúdo do arquivo. Arquivos binários - ii 38 São usados para armazenar diversos tipos de informação, como imagens, vídeos, executáveis, etc. O nome binário se dá pelo fato que ele é tratado como uma sequência de bits (ou bytes, dependendo o caso), em formato binário. Arquivos binários e memória Arquivos binários permitem gravar os dados como são representados na memória. Ex: struct aluno { long matricula; char nome [10]; char cpf[10]; char entrada; long curso; }; aluno jose; ↤ matrícula ↦ ↤ nome ↦ ↤ cpf ↦ ↤ curso ↦ entrada 39 Arquivos binários e memória Arquivos binários permitem gravar os dados como são representados na memória. Ex: struct aluno { long matricula; char nome [10]; char cpf[10]; char entrada; long curso; }; aluno jose; ↤ matrícula ↦ ↤ nome ↦ ↤ cpf ↦ ↤ curso ↦ entrada 40 Toda a estrutura seria gravada no arquivo de uma única vez, copiando a região da memória diretamente para o arquivo. Características de arquivos binários A. Em um arquivo binário como os dados são salvos no mesmo formato que estão na memória, a leitura e escrita dos arquivos é mais rápida, uma vez que não são necessárias conversões de e para texto. B. Como é gravado ou lido um vetor de bytes de um tamanho especificado, é possível gravar ou ler vários dados de um única vez, sendo possível ler ou gravar um vetor inteiro, por exemplo. C. Dados em formato binário ocupam o tamanho ocupado em memória, ou seja, o tamanho do tipo da variável. Um inteiro, por exemplo, seria armazenado com quatro bytes, indiferente do seu valor (seja 4, seja 31.428 ou 19.454.324). 41 Arquivos tipados Um tipo específico de arquivo binário que é muito utilizado é o arquivo tipado, que consiste geralmente em sequência de registros. Como todos registros ocupam o mesmo espaço na memória, é relativamente fácil manipular esse tipo de arquivo, pulando posições, inclusive. Para que isso seja possível, entretanto, não é possível usar campos que sejam dinâmicos, como string ou vector. 42 Streams binárias Para o Sistema Operacional, os dados de arquivos são obtidos em bloco, como se fossem uma sequência (stream) de bytes. Exemplo: #include <unistd.h> #include <fcntl.h> int main(){ int fd; fd = open("teste.txt", O_CREAT|O_RDWR); write(fd, "teste\n", 7); close(fd); return 0; } 43 C++ e streams binárias Os tipos fstream, ifstream e ofstream suportam métodos para leitura e escrita de sequências de bytes em arquivos. Ex: ifstream entrada("teste.txt"); ofstream saida("resultado.txt"); char teste[10]; entrada.read(teste,10); saida.write("teste\n",7); 44 Exemplo de uso de stream binária Dado um arquivo qualquer informado pelo usuário, não importando se arquivo texto ou arquivo binário, pede-se para informar quantas vezes aparecem os caracteres ‘a’ ou ‘A’ nesse arquivo 45 46 #include <fstream> #include <iostream> using namespace std; int main(){ string nomeArq; cin >> nomeArq; ifstream arquivo(nomeArq.c_str()); char c[1]; int count = 0; if (arquivo) { while ( arquivo.read(c,1) ){ if ( (c[0] == 'a') or (c[0] == 'A') ) count++; } cout << count << endl; } else cout << "arquivo não pode ser aberto!" << endl; return 0; } 47 #include <fstream> #include <iostream> using namespace std; int main(){ string nomeArq; cin >> nomeArq; ifstream arquivo(nomeArq.c_str()); char c[1]; int count = 0; if (arquivo) { while ( arquivo.read(c,1) ){ if ( (c[0] == 'a') or (c[0] == 'A') ) count++; } cout << count << endl; } else cout << "arquivo não pode ser aberto!" << endl; return 0; } O método c_str() de strings retorna o seu texto como um vetor de caracteres. Versões mais recentes do compilador C++ aceitam diretamente strings na abertura de arquivos. 48 #include <fstream> #include <iostream> using namespace std; int main(){ string nomeArq; cin >> nomeArq; ifstream arquivo(nomeArq.c_str()); char c[1]; int count = 0; if (arquivo) { while ( arquivo.read(c,1) ){ if ( (c[0] == 'a') or (c[0] == 'A') ) count++; } cout << count << endl; } else cout << "arquivo não pode ser aberto!" << endl; return 0; } Os métodos read() e write() de arquivos possuem um vetor de caracteres como parâmetro. Assim, usamos um vetor de um único caractere neste caso. 49 #include <fstream> #include <iostream>using namespace std; int main(){ string nomeArq; cin >> nomeArq; ifstream arquivo(nomeArq.c_str()); char c[1]; int count = 0; if (arquivo) { while ( arquivo.read(c,1) ){ if ( (c[0] == 'a') or (c[0] == 'A') ) count++; } cout << count << endl; } else cout << "arquivo não pode ser aberto!" << endl; return 0; } Escrevendo dados binários em arquivos Podemos acessar a memória como se fosse um vetor de bytes (caracteres) e gravar os dados acessados em arquivo: arquivo.write((const char *) (&variavel), sizeof(tipo_dado_variavel)); ou arquivo.write(reinterpret_cast<const char *> (&variavel), sizeof(tipo_dado_variavel)); 50 Escrevendo dados binários em arquivos Podemos acessar a memória como se fosse um vetor de bytes (caracteres) e gravar os dados acessados em arquivo: arquivo.write((const char *) (&variavel), sizeof(tipo_dado_variavel)); ou arquivo.write(reinterpret_cast<const char *> (&variavel), sizeof(tipo_dado_variavel)); Observação: char * é a notação com ponteiros para indicar um vetor de caracteres (bytes) alocado dinamicamente ou cujo tamanho não encontra-se especificado. 51 Escrevendo dados binários em arquivos Podemos acessar a memória como se fosse um vetor de bytes (caracteres) e gravar os dados acessados em arquivo: arquivo.write((const char *) (&variavel), sizeof(tipo_dado_variavel)); ou arquivo.write(reinterpret_cast<const char *> (&variavel), sizeof(tipo_dado_variavel)); O & é utilizado para informar o endereço da variável, com isso os dados são acessados diretamente na memória. 52 Lendo dados binários de arquivos Os dados em arquivos binários são lidos como vetores de bytes (caracteres): arquivo.read((char *) (&variavel), sizeof(tipo_dado_variavel)); ou arquivo.read(reinterpret_cast<char *> (&variavel), sizeof(tipo_dado_variavel)); 53 Exemplo 1 aluno jose; arquivo1.read((char *) (&jose), sizeof(aluno)); arquivo2.write((const char *) (&jose), sizeof(aluno)); 54 Exemplo 2 int vetor[10]; arquivo1.read((char *) (vetor), sizeof(int) * 10); arquivo2.write((const char *) (vetor), sizeof(vetor)); 55 Posição de leitura e escrita - i A posição de leitura e escrita (E/S) pode ser acessada pelas funções tellp() e tellg() e modificada pelas funções seekp() e seekg(). struct aluno { ... }; // colocando a posição de leitura no terceiro registro arquivo.seekg(2*sizeof(aluno)) 56 Posição de leitura e escrita - ii seekg: posiciona a cabeça de leitura para o próximo caracter a ser extraído do stream de entrada seekg (streampos pos); //pos: posição absoluta dentro do stream seekg (streamoff off, ios_base::seekdir way); //off: deslocamento dentro do stream a partir da posição indica pelo retorno de way //way: indica posição do arquivo onde começará a busca 57 Posição de leitura e escrita - iii Para ir para início ou fim do arquivo é possível utilizar os campos beg e end do arquivo: // posicionando no final do arquivo arquivo.seekg (0, arquivo.end); // retornando ao início do arquivo arquivo.seekg (0, arquivo.beg); 58 Posição de leitura e escrita - iv 59 Saída da execução Exemplo 1: Posição de leitura e escrita - v 60 Saída da execução Exemplo 2: 61 #include <fstream> #include <iostream> using namespace std; int main(){ ifstream entrada("teste.txt"); if (entrada) { int tam = entrada.tellg(); cout << tam << endl << endl; } else cout << "arquivo não pode ser aberto!" << endl; return 0; } tellg - retorna posição do caracter atual no stream de entrada Função tellg() Saída da execução 0 teste.txt Exemplo - posição de leitura e escrita Crie um programa que lê todo o conteúdo de um arquivo texto de uma única vez, imprimindo antes a quantidade de bytes (caracteres) utilizados nesse arquivo. 62 63 #include <fstream> #include <iostream> using namespace std; int main(){ ifstream entrada("teste.txt"); if (entrada) { entrada.seekg (0, entrada.end); int tam = entrada.tellg(); entrada.seekg (0, entrada.beg); cout << tam << endl << endl; char bloco[tam]; entrada.read (bloco,tam); cout.write (bloco,tam); } else cout << "arquivo não pode ser aberto!" << endl; return 0; } 64 #include <fstream> #include <iostream> using namespace std; int main(){ ifstream entrada("teste.txt"); if (entrada) { entrada.seekg (0, entrada.end); int tam = entrada.tellg(); entrada.seekg (0, entrada.beg); cout << tam << endl << endl; char bloco[tam]; entrada.read (bloco,tam); cout.write (bloco,tam); } else cout << "arquivo não pode ser aberto!" << endl; return 0; } Vai até o final do arquivo, pega a posição (número de bytes) e retorna ao início. 65 #include <fstream> #include <iostream> using namespace std; int main(){ ifstream entrada("teste.txt"); if (entrada) { entrada.seekg (0, entrada.end); int tam = entrada.tellg(); entrada.seekg (0, entrada.beg); cout << tam << endl << endl; char bloco[tam]; entrada.read (bloco,tam); cout.write (bloco,tam); } else cout << "arquivo não pode ser aberto!" << endl; return 0; } Aloca um bloco com o tamanho do arquivo para sua leitura em um único comando. 66 #include <fstream> #include <iostream> using namespace std; int main(){ ifstream entrada("teste.txt"); if (entrada) { entrada.seekg (0, entrada.end); int tam = entrada.tellg(); entrada.seekg (0, entrada.beg); cout << tam << endl << endl; char bloco[tam]; entrada.read (bloco,tam); cout.write (bloco,tam); } else cout << "arquivo não pode ser aberto!" << endl; return 0; } Usamos o método write() do cout, de maneira similar ao uso em arquivos. 67 #include <fstream> #include <iostream> using namespace std; int main(){ ifstream entrada("teste.txt"); if (entrada) { entrada.seekg (0, entrada.end); int tam = entrada.tellg(); entrada.seekg (0, entrada.beg); cout << tam << endl << endl; char bloco[tam]; entrada.read (bloco,tam); cout.write (bloco,tam); } else cout << "arquivo não pode ser aberto!" << endl; return 0; } Sobre o Material 68 Sobre este material Material produzido coletivamente, principalmente pelos seguintes professores do DCC/UFLA: ● Janderson Rodrigo de Oliveira ● Joaquim Quinteiro Uchôa ● Juliana Galvani Greghi ● Renato Ramos da Silva Inclui contribuições de outros professores do setor de Fundamentos de Programação do DCC/UFLA. Esta obra está licenciado com uma Licença Creative Commons Atribuição 4.0 Internacional. 69 http://creativecommons.org/licenses/by/4.0/
Compartilhar