Baixe o app para aproveitar ainda mais
Prévia do material em texto
1 Arquivos Um arquivo é um conjunto de registros gravados permanentemente em um dispositivo de memória secundária (de armazenamento externo), como por ex, disquete, Winchester, Unidade de Fita DAT, entre outros, pode ser lido e escrito por um programa.. A utilização de arquivos trás a vantagem de não ser necessário indicar a quantidade de registros que um arquivo contem, pois, como ele é gravado em um dispositivo de armazenamento externo, o computador não precisa reservar espaço de memória. Dessa forma, o espaço é alterado de acordo com a necessidade, diferentemente dos conjuntos de registros do vetor que, utilizados ou não, têm um determinado espaço reservado de memória. São Estruturas de Dados manipuladas fora do ambiente do programa (memória principal). É formado por uma coleção de registros, cada registro é composto por campos e cada campo possui suas características específicas. Um ou mais campos de um registro é considerado campo-chave, que é o campo que diferencia um registro dos demais evitando duplicidade de informações. Um sistema de banco de dados é composto por um ou vários arquivos, onde cada arquivo possui programas de manutenção, que são: inclusão, exclusão lógica ou exclusão física, alteração, consulta geral, consulta específica e relatórios. Existem dois tipos de exclusão a registros: a exclusão física, em que após a eliminação de um registro, os demais registros são deslocados, e a exclusão lógica, em que os registros possuem um campo adicional, identificando se os mesmos estão ativos ou inativos, isto é, se foram excluídos. Os arquivos podem ser acessados para leitura e/ou escrita na forma seqüencial, direta ou indexada: 2 • Acesso Seqüencial – ocorre quando o processo de gravação e leitura é feito de forma contínua, um arquivo após o outro. Dessa forma, para gravar um novo registro, é necessário percorrer todo o arquivo a partir do primeiro registro, registro a registro, até localizar a primeira posição vazia após o último registro. O processo de leitura também ocorrer de forma seqüencial. Se o registro a ser lido for o último, primeiro é necessário ler todos os registros que o antecedem. • Acesso Direto – conhecido também por acesso randômico, ocorre por meio de um campo-chave previamente definido. Dessa forma, passa-se a possuir um vínculo entre um dos campos do registro e sua posição de armazenamento, por intermédio da chave. Assim sendo, o acesso a um registro tanto para leitura quanto para escrita pode ser feito de forma instantânea. Isso implica no fato no fato de que os registros de um arquivo direto possuem um lugar (chave) previamente “reservado” para serem armazenados. • Acesso Indexado – Ocorre quando se acessa de forma direta um arquivo seqüencial. Na sua maioria, todo arquivo criado armazena os registros de forma seqüencial. O modo de acesso seqüencial se torna incoveniente, pois a medida que o arquivo aumenta de tamanho, aumenta também o tempo de acesso a ele. Para trabalhar com essa técnica, basta criar um arquivo direto que será o índice de consulta do arquivo seqüencial, passando a ser arquivo indexado. Assim sendo, existirão dois arquivos: o arquivo ínidice e o indexado (arquivo seqüencial). Os acessos são feitos como em um livro. Primeiro se consulta o arquivo índice, o qual possui a chave de pesquisa, no caso seria o número da página, depois basta se posicionar de forma direta na página identificada no índice, ou seja, no registro do arquivo indexado. 3 Declaração de arquivos em C++ Um arquivo em C++ pode representar diversas coisas, como arquivos em disco, uma impressora, um teclado, ou qualquer dispositivo de entrada ou saída. É importante estabelecer que o conceito de arquivo é uma estrutura utilizada para o armazenamento de dados. Por este motivo, as operações com arquivos realizadas pela linguagem de programação C++ são todas baseadas na manipulação de objetos, específicos para essa finalidade. Acesso a Arquivos O acesso a manipulação de arquivos com a linguagem de programação C++ é efetuado por meio do arquivo de cabeçalho fstream.h, com as classes: • ofstream (out file stream – definição para arquivo de saída): Essa classe é utilizada para declarar um objeto do tipo arquivo que será utilizado em operações de saída de dados da memória principal para a memória secundária, ou seja, para operações em um determinado conteúdo será escrito dentro do arquivo especificado. • ifstream (in file stream – definição para arquivo de saída): Essa classe é utilizada para declarar um objeto do tipo arquivo que será utilizado em operações de entrada de dados da memória secundária para a memória principal, ou seja, para operações em que um determinado conteúdo será lido de dentro do arquivo especificado; • fstream (file stream – definição para arquivo de entrada e saída): Essa classe é utilizada para declarar um objeto do tipo arquivo que será utilizado para operações de entrada e de saída em um mesmo arquivo; As classes ofstream, ifstream e fstream possuem alguns métodos de controle que visam auxiliar as operações de acesso aos objetos do tipo arquivo. Esses métodos serão apresentados à medida que se fizerem necessários. 4 Tipos de Arquivos O sistema operacional MS-DOS distingue arquivos do tipo texto (formato ASCII) de arquivos do tipo binário. Existem sistemas operacionais em que não se encontra essa distinção, o que de carta forma é mais adequado, visto que um arquivo em formato texto nada mais é do que um arquivo em formato binário delimitado por uma faixa de caracteres que pode ser nele armazenada. Arquivo do Tipo Texto Os arquivos do tipo texto estão capacitados a armazenar caracteres, palavras, frases e também dados numéricos. Os números, entretanto, serão armazenados como caracteres do tipo alfanumérico, e desta forma ocuparão muito mais espaço em disco do que ocupariam na memória de um computador se fossem definidos como tipos numéricos. A solução para este detalhe é utilizar funções que manipulem os arquivos em formato binário, que serão apresentadas mais adiante. Antes de iniciar qualquer operação com arquivo, é ideal que o arquivo seja primeiramente criado. Isso pode ser realizado em linguagem de programação C++ de várias maneiras, uma delas é com a utilização do especificador ios::ttrunc. // parq_01.CPP #include <fstream> #include <cstdlib> using namespace std; int main(void) { ofstream arq; arq.open("arquivo.txt", ios::trunc); arq.close(); return 0; } 5 Após a elaboração do programa, execute-o para que o arquivo arquivo.txt seja criado. Observe que na primeira linha do programa ocorre a instância o objeto arq a partir da classe fstream, a qual estabelece para o referido objeto o tipo de arquivo texto. Em seguida é utilizada a instrução arq.open(“arquivo.txt”, ios::trunc) que efetua a associação do nome do arquivo arquivo.txt com o objeto arquivo, colocando o arquivo em modo open e estabelecendo o modo de abertura através do especificador ios::trunc, o qual cria o arquivo propriamente dito, caso ele não exista. Se existir um arquivo com o mesmo nome, ele será apagado e um novo arquivo será criado. Por fim é utilizada a instrução arquivo.close(); que efetua o fechamento do arquivo criado. Para verificar a criação do referido arquivo, solicite a apresentação do conteúdo do diretório de seu disco. Observe a apresentação do arquivo arquivo.txt recentemente criado com zero byte. Outra forma de criar um arquivo é utilizando o especificador ios::noreplace no lugar do especificador ios::trunc. Nesse caso, o arquivo será criado caso esse não exista. Se o arquivo existir, a operação de abertura falhará. 6 // parq_02.CPP #include<iostream> #include <fstream> #include <cstdlib> using namespace std; int main(void) { char mensagem[51]; ofstream arq; arq.open("arquivo.txt", ios::app); cout << "Informe uma mensagem com ate 50 caracteres" << endl; cout << "--> "; cin.getline(mensagem, sizeof(mensagem)); arq << mensagem << endl; arq.close(); return 0; } O programa anterior estabelece uma variável MENSAGEM do tipo char com a capacidade de armazenar até 50 caracteres. Em seguida é efetuada a abertura do arquivo com a instrução arq.open(“arquivo.txt”, ios::app);, que abre o arquivo arq.txt para a operação de inclusão de uma nova mensagem, posicionando o ponteiro de controle de registros sempre no final do arquivo, graças ao especificador ios::app, que faz o arquivo operar de modo append (inserção). Dessa forma, se o arquivo já possuir alguma mensagem cadastrada, a próxima mensagem será colocada após a última cadastrada. A instrução cin.getline(MENSAGEM, sizeof(MENSAGEM)); solicita a entrada de uma mensagem, e a instrução arq << MENSAGEM <<endl; faz a gravação da mensagem no arquivo representado pelo objeto arq. 7 // parq_03.CPP #include <iostream> #include <fstream> #include <cstdlib> using namespace std; int main(void) { char mensagem; ifstream arq; arq.open("arquivo.txt", ios::in); while (!arq.eof()) { mensagem = arq.get(); cout.put(mensagem); } arq.close(); system("pause"); return 0; } Observe que para a operação de extração do conteúdo de um arquivo está sendo utilizada a classe ifstream e não a classe ofstream, como no programa anterior. A classe ifstream coloca o arquivo em modo de leitura, enquanto a classe ofstream coloca o arquivo em modo de escrita. Em seguida o programa estabelece a variável MENSAGEM do tipo char com a capacidade de armazenar apenas um caractere. Esta variável será usada para a obtenção caractere a caractere armazenado no arquivo. Depois é efetuada a abertura do arquivo com a instrução arq.open(“arquivo.txt”, ios::in);, que abre o arquivo arquivo.txt para a operação de leitura dos seus dados, posicionando o ponteiro de controle no primeiro caractere da primeira linha do arquivo, graças ao especificador ios::in, pertencente à classe ifstream. Para efetuar a leitura, está sendo definido um laço que executará a ação de leitura enquanto o final do arquivo não for alcançado, através da instrução 8 while (!arq.eof()). Observe o uso do método eof (end of file – fim do arquivo) que auxilia na tarefa de identificação do final de arquivo. Note também que a leitura ocorrerá caractere a caractere. Nesse caso a instrução MENSAGEM = arq.get();, fará a leitura de apenas um caractere do arquivo, o qual será apresentado pela instrução cout.put(MENSAGEM);. Ambas as instruções serão realizadas até que o final do arquivo seja atingido. Assim sendo, elas apresentarão a mensagem (ou mensagens) de forma completa. Arquivo do Tipo Binário Nada impede que sejam armazenados números em arquivos do tipo texto, deve-se levar em consideração, como mencionado anteriormente, que um número gravado como um caractere alfanumérico num arquivo do tipo texto ocupa um espaço maior do que se fosse armazenado em um arquivo do tipo binário. Outro detalhe a ser considerado é o fato de que os arquivos do tipo texto são adequados para serem manipulados por programas de edição de textos, por esta razão tem seu nível de segurança questionado. Os arquivos binários são úteis quando há necessidade de trabalhar com informações de uso limitado, pois um arquivo do tipo binário não pode ser manipulado por programas de edição de textos. Os arquivos do tipo binário permitem o armazenamento de estruturas e classes, em que normalmente se utilizam dados heterogêneos. A seguir, serão apresentados dois exemplos de programas para ilustrar o uso de arquivos binários com a manipulação de dados do tipo float a partir de uma matriz de uma dimensão. Um arquivo do tipo binário é indicado pelo especificador ios::binary. 9 // parq_04.CPP #include <iostream> #include <iomanip> #include <fstream> #include <cstdlib> using namespace std; int main(void) { int I; float A[10]; ofstream arq; arq.open("arqbin.txt", ios::out | ios::binary); for (I = 0; I < 10; I++) { cout << "Digite o elemento " << setw(2) << I + 1 << " - "; cin >> A[I]; } arq.write((char*)&A, sizeof(float)*I-1); arq.close(); return 0; } Após a elaboração do programa, execute-o para que o arquivo arqbin.txt seja criado através da instrução arq.open(“arqbin.txt”, ios::out | ios::binary);. Observe que neste exemplo o arquivo não está sendo criado previamente. O próprio método open faz a criação do arquivo se ele não existir, desde que o modo open esteja configurado para saída através do manipulador ios::out. Note que, além do manipulador ios::out, está sendo utilizado o manipulador ios::binary, que é responsável por estabelecer o uso de um arquivo em formato binário. Perceba que os dois manipuladores estão separados por intermédio do símbolo “|” (pipe). Após a criação e abertura do arquivo, o programa efetua o carregamento de dez valores float para a matriz A. após este procedimento, através da instrução arq.write((char *)&A, sizeof(A));, o programa efetua a gravação de 10 todos os valores de uma só vez dentro do arquivo. Note que para essa operação será usado o método write, que utiliza dois parâmetros, sendo o primeiro o endereço inicial em que se encontra o primeiro elemento da matriz e o segundo o tamanho ocupado para armazenamento de todos os valores. // parq_05.CPP #include <iostream> #include <fstream> #include <cstdlib> using namespace std; int main(void) { int I; float A[10]; ifstream arq; arq.open("arqbin.txt", ios::in | ios::binary); arq.read((char *)&A, sizeof(A)); for (I = 0; I < 10; I++) { cout << "Elemento " << setw(2) << I + 1 << " = "; cout << A[I] << endl; } arq.close(); system("pause"); return 0; } Observe que o programa anterior efetua a leitura do conteúdo do arquivo através da instrução arq.read((char *)&A, sizeof(A));, que efetua o carregamento dos valores armazenados na matriz A. em seguida, o programa apresenta os valores lidos e armazenados na matriz. Perceba o uso do método read. 11 Modos de Abertura de Arquivos Anteriormente foram apresentados alguns exemplos de manipulação de arquivos de formato texto e binário de modo básico. Para os exemplos foram realizadas as aberturas dos arquivos com o método open: ofstream arq; arq.open(“aquivo.txt”, ios::trunc); ofstream arq; arq.open(“aquivo.txt”, ios::app); ifstream arq; arq.open(“aquivo.txt”, ios::in); ofstream arq; arq.open(“aquivo.txt”, ios::out | ios::binary); ifstream arq; arq.open(“aquivo.txt”, ios::in | ios::binary); No entanto, o método open pode ser suprimido, bastando para isso associar a sua operação diretamente à indicação da classe ofstream ou ifstream, ou seja, basta usar o modo construtor de cada classe para estabelecer automaticamente o modo open de trabalho. Ao utilizar a declaração dos construtores das classes ofstream e ifstream, ocorre a eliminação automática do uso do método open. Esta estratégia permite melhorar a legibilidade do programa, pois diminui o número de declarações utilizadas no programa. ofstream arq.open(“aquivo.txt”, ios::trunc); 12 ofstream arq.open(“aquivo.txt”, ios::app); ifstream arq.open(“aquivo.txt”, ios::in); ofstream arq.open(“aquivo.txt”, ios::out | ios::binary); ifstream arq.open(“aquivo.txt”, ios::in | ios::binary);Arquivo de Entrada e Saída Até o presente momento estão sendo utilizadas as classes ifstream e ofstream para a realização das operações de entrada e de saída em arquivos tanto do tipo texto como do tipo binário de forma separadas. No entanto, há a possibilidade de utilizar a classe fstream para realizar as operações de entrada e de saída em arquivos de uma forma mais dinâmica. A classe fstream herda todos os recursos das classes ifstream e ofstream. Dessa forma, é possível realizar com fstream qualquer operação de entrada ou de saída realizada anteriormente com as classes ifstream e ofstream. Utilizar apenas uma classe para a realização de acesso a arquivos é mais prático e eficiente. 13 // parq_06.CPP #include <iostream> #include <iomanip> #include <fstream> #include <cstdlib> using namespace std; int main(void) { int I, A[10]; fstream arq("arquivo.txt", ios::out | ios::binary); for (I = 0; I < 10; I++) { cout << "Digite o elemento " << setw(2) << I + 1 << " - "; cin >> A[I]; } arq.write((char *)&A, sizeof(A)); arq.close(); return 0; } Note o uso da linha de código fstream arq(“arquivo.txt”, ios::out | ios::binary); com o construtor da classe fstream, em vez de utilizar o construtor ofstream, como foi efetuado em exemplos anteriores. Atente para o uso do especificador ios::out 14 // parq_07.CPP #include <iostream> #include <iomanip> #include <fstream> #include <cstdlib> using namespace std; int main(void) { int I, A[10]; fstream arq("arquivo.txt", ios::in | ios::binary); arq.read((char *)&A, sizeof(A)); for (I = 0; I < 10; I++) { cout << "Elemento " << setw(2) << I + 1 << " = "; cout << A[I] << endl; } arq.close(); system("pause"); return 0; } Note o uso da linha de código fstream arq(“arquivo.txt”, ios::in | ios::binary);, com o construtor da classe fstream, em vez de utilizar o construtor ifstream, como foi efetuado em exemplos anteriores. Atente para o uso do especificador ios::in. Observe que torna-se mais fácil utilizar apenas uma classe para tratar os arquivos, modificando apenas o especificador desejado. 15 // parq_08.CPP #include <iostream> #include <iomanip> #include <fstream> #include <cstdlib> using namespace std; int main(void) { int I, A[10]; fstream arq("arquivo.txt", ios::in | ios::out | ios::binary); cout << "Entrada de dados\n" << endl; for (I = 0; I < 10; I++) { cout << "Digite o elemento " << setw(2) << I + 1 << " - "; cin >> A[I]; } arq.write((char *)&A, sizeof(A)); cout << "Saida de dados\n" << endl; arq.read((char *)&A, sizeof(A)); for (I = 0; I < 10; I++) { cout << "Elemento " << setw(2) << I + 1 << " = "; cout << A[I] << endl; } arq.close(); system("pause"); return 0; } Observe atentamente a linha de comando que utiliza a classe fstream. Observe a indicação dos especificadores de entrada ios::in e de saída ios::out separados pelo símbolo pipe. 16 // parq_09.CPP #include <iostream> #include <iomanip> #include <fstream> #include <cstdlib> using namespace std; int main(void) { int I, A[10]; ofstream arq1("arquivo.txt", ios::out | ios::binary); ifstream arq2("arquivo.txt", ios::in | ios::binary); cout << "Entrada de dados\n" << endl; for (I = 0; I < 10; I++) { cout << "Digite o elemento " << setw(2) << I + 1 << " - "; cin >> A[I]; } arq1.write((char *)&A, sizeof(A)); arq1.close(); cout << "Saida de dados\n" << endl; arq2.read((char *)&A, sizeof(A)); for (I = 0; I < 10; I++) { cout << "Elemento " << setw(2) << I + 1 << " = "; cout << A[I] << endl; } arq2.close(); system("pause"); return 0; } 17 Veja que a vantagem em utilizar a classe fstream é a possibilidade de não precisar instanciar mais do que um objeto do tipo arquivo para realizar as operações de entrada e de saída em um mesmo arquivo. Comparando os 2 programas anteriores, nota-se quão mais fácil se torna o trabalho ao utilizar a classe fstream. Note que foram necessários instanciar dois objetos (arq1 e arq2) para tratar o arquivo de dados arquivo.txt. Por questões de praticidade, será adotada doravante a classe fstream para a manipulação de arquivos. Controle de Operações As operações de entrada e saída em arquivos podem ser verificadas quanto ao seu sucesso de operação. Anteriormente já foi apresentada a função (método) eof() que tem por finalidade verificar o final de um determinado arquivo. Além do método eof(), para auxiliar essa tarefa, existem ainda os métodos: good(), re torna o valor 1 caso a operação ocorra de forma satisfatória e fail(), que retorna o valor 1 caso ocorra uma falha na operação. Observe que o especificador ios::nocreate tenta abrir o arquivo sem criá- lo (no create). Dessa forma, essa operação somente será válida se o arquivo realmente existir. Caso o arquivo não exista, a ação falha. A falha que ocorre na tentativa de abertura de um arquivo inexistente pode ser interceptada pelo método fail(), como mostra o programa. Outro detalhe usado nesse programa é a definição do fluxo de saída cerr, ainda não utilizado. O fluxo cerr é normalmente utilizado quando se deseja apresentar no dispositivo de saída padrão (tela) uma mensagem de erro. Nada 18 impediria de ter sido utilizado o fluxo de saída cout, mas esta foi uma oportunidade para apresentar outro fluxo de saída. Observe que o especificador ios::noreplace tenta criar o arquivo sem sobrepô-lo (no replace). Dessa forma, essa operação somente será válida se o arquivo realmente não existir. Caso o arquivo exista, a ação falhará. A falha que ocorre na tentativa de criação de um arquivo já existente no sistema pode ser interceptada pelo método fail(), como mostra o programa. Note que os métodos de controle de operações permitem estabelecer algumas ações interessantes. Imagine uma rotina que tente abrir um arquivo e se ele não existir, ela o cria. O programa seguinte executa essa tarefa. Ao ser executado, o programa tentará abrir o arquivo DADOS.PPP; caso o arquivo não possa ser aberto, o programa irá então criá-lo. Esta é uma das formas de fazer a verificação de acesso a arquivos com a linguagem de programação C++.
Compartilhar