Como vimos nas aulas anteriores, para alterar qualquer registro em um arquivo texto, todo seu conteúdo precisa ser carregado para a memória e, depois de alterado, todo o conteúdo precisa ser salvo novamente no arquivo. Esse processo é custoso. Em um arquivo binário esse processo de atualização é mais eficiente, uma vez que podemos ler e salvar apenas o registro a ser alterado. É isso que veremos na aula de hoje, como atualizar um registro em um arquivo binário com a função fseek.
Para isso precisamos primeiro ler todo o arquivo e imprimir os dados na tela com um índice para cada contato, perguntando ao usuário qual contato ele deseja alterar (aqui vale a mesma regrinha utilizada para vetor e matriz, o primeiro registro está na posição zero do arquivo).
FILE *file = fopen(arq, "rb+"); Contato c; int id, i = 1; if(file){ printf("\tLista de contatos:\n"); printf("\t----------------------------\n"); while(fread(&c, sizeof(Contato), 1, file)){ printf("\t%d = %2d/%2d/%4d\t%s\n", i, c.dia, c.mes, c.ano, c.nome); i++; } printf("\t----------------------------\n"); ...
Ao ler o índice digitado precisamos garantir que seja um índice válido , para então posicionar o ponteiro do nosso arquivo na posição correta, onde está o contato escolhido pelo usuário, e então ler e salvar os novos dados do contato. Perceba que, diferentemente de um arquivo texto, aqui é possível localizar e alterar um contato diretamente no arquivo, sem precisar reescrever todos os contatos.
Como ao imprimir os Contatos eu iniciei pelo índice 1 ( i = 1 ), quando o usuário digitar o contato que ele deseja alterar é necessário subtrair uma unidade. Assim, se o usuário digitar 1, significa que ele deseja alterar o primeiro Contato, subtraindo 1, temos o índice zero, que corresponde ao primeiro Contato no arquivo.
// lendo o índice do Contato a ser alterado printf("\n\tDigite o indice do contato que deseja alterar:\n\n"); scanf("%d", &id); getchar(); id--;
Como imprimimos todo o conteúdo do arquivo, nosso ponteiro file está apontando para o fim do arquivo. É necessário fazer ele apontar exatamente para o bloco de bytes onde está o contato que o usuário deseja alterar. Podemos fazer isso com a função fseek. Esta função realiza um deslocamento no arquivo a partir do seu início. Assim, imaginando que o usuário tenha digitado 1 e, após a subtração tenhamos o índice zero, esta função fará um deslocamento de zero bytes (id * sizeof(Contatos)) a partir do início do arquivo, ou seja, posicionando o ponteiro exatamente na região do primeiro Contato.
// posicionando o ponteiro file na direção do Contato a ser alterado fseek(file, id * sizeof(Contato), SEEK_SET);
Procedimento completo em C para alterar um Contato em um arquivo binário
// procedimento para alterar um contato void alterarB(char arq[]){ FILE *file = fopen(arq, "rb+"); Contato c; int id, i = 1; if(file){ printf("\tLista de contatos:\n"); printf("\t----------------------------\n"); while(fread(&c, sizeof(Contato), 1, file)){ printf("\t%d = %2d/%2d/%4d\t%s\n", i, c.dia, c.mes, c.ano, c.nome); i++; } printf("\t----------------------------\n"); printf("\n\tDigite o indice do contato que deseja alterar:\n\n"); scanf("%d", &id); getchar(); id--; if(id >= 0 && id < i - 1){ printf("\tDigite nome e data de nascimento dd mm aaaa: "); scanf("%49[^\n]%d%d%d", c.nome, &c.dia, &c.mes, &c.ano); fseek(file, id * sizeof(Contato), SEEK_SET); fwrite(&c, sizeof(Contato), 1, file); } fclose(file); } else printf("\nErro ao abrir arquivo!\n"); }
Código completo em C para a agenda de aniversários com suporte a arquivo texto e arquivo binário
/* AGENDA DE ANIVERSÁRIOS Código escrito por Wagner Gaspar Junho de 2021 */ #include <stdio.h> #include <stdlib.h> typedef struct{ char nome[50]; int dia, mes, ano; }Contato; void imprimir(Contato **c, int quant){ int i; printf("\n\t\tLista de Contatos:\n"); printf("\t--------------------------------\n"); for(i = 0; i < quant; i++){ printf("\t%d = %2d/%2d/%4d\t%s\n", i+1, c[i]->dia, c[i]->mes, c[i]->ano, c[i]->nome); } printf("\t--------------------------------\n"); } int cadastrar_contato(Contato **c, int quant, int tam){ if(quant < tam){ Contato *novo = malloc(sizeof(Contato)); printf("\nDigite o nome do contato: "); scanf("%49[^\n]", novo->nome); printf("\nDigite a data de aniversario dd mm aaaa: "); scanf("%d%d%d", &novo->dia, &novo->mes, &novo->ano); getchar(); c[quant] = novo; return 1; } else{ printf("\n\tImpossivel novo cadastro. Vetor cheio!\n"); return 0; } } void alterar_contato(Contato **c, int quant){ int id; imprimir(c, quant); printf("\n\tDigite o codigo do contato que deseja alterar: \n"); scanf("%d", &id); getchar(); id--; if(id >= 0 && id < quant){ Contato *novo = malloc(sizeof(Contato)); printf("\nDigite o nome do contato: "); scanf("%49[^\n]", novo->nome); printf("\nDigite a data de aniversario dd mm aaaa: "); scanf("%d%d%d", &novo->dia, &novo->mes, &novo->ano); getchar(); c[id] = novo; } else printf("\n\tCodigo invalido!\n"); } void salvar(Contato **c, int quant, char arq[]){ FILE *file = fopen(arq, "w"); int i; if(file){ fprintf(file, "%d\n", quant); for(i = 0; i < quant; i++){ fputs(c[i]->nome, file); fputc('\n', file); fprintf(file, "%d %d %d\n", c[i]->dia, c[i]->mes, c[i]->ano); } fclose(file); } else printf("\n\tNAO FOI POSSIVEL ABRIR/CRIAR O ARQUIVO!\n"); } int ler_arquivo(Contato **c, char arq[]){ FILE *file = fopen(arq, "r"); int quant = 0, i; Contato *novo = malloc(sizeof(Contato)); if(file){ fscanf(file, "%d\n", &quant); for(i = 0; i < quant; i++){ //fgets(novo->nome, 50, file); fscanf(file, "%50[^\n]", novo->nome); fscanf(file, "%d %d %d\n", &novo->dia, &novo->mes, &novo->ano); c[i] = novo; novo = malloc(sizeof(Contato)); } fclose(file); } else printf("\n\tNAO FOI POSSIVEL ABRIR/CRIAR O ARQUIVO!\n"); return quant; } void salvarB(char arq[], Contato **c, int quant){ FILE *file = fopen(arq, "wb"); int i; if(file){ for(i = 0; i < quant; i++) fwrite(c[i], sizeof(Contato), 1, file); fclose(file); } else printf("\nErro ao abrir arquivo!\n"); } int lerB(char arq[], Contato **c){ int quant = 0; Contato *novo = malloc(sizeof(Contato)); FILE *file = fopen(arq, "rb"); if(file){ while(fread(novo, sizeof(Contato), 1, file)){ c[quant] = novo; quant++; novo = malloc(sizeof(Contato)); } fclose(file); } else printf("\nErro ao abrir arquivo!\n"); return quant; } void alterarB(char arq[]){ FILE *file = fopen(arq, "rb+"); Contato c; int id, i = 1; if(file){ printf("\tLista de contatos:\n"); printf("\t----------------------------\n"); while(fread(&c, sizeof(Contato), 1, file)){ printf("\t%d = %2d/%2d/%4d\t%s\n", i, c.dia, c.mes, c.ano, c.nome); i++; } printf("\t----------------------------\n"); printf("\n\tDigite o indice do contato que deseja alterar:\n\n"); scanf("%d", &id); getchar(); id--; if(id >= 0 && id < i - 1){ printf("\tDigite nome e data de nascimento dd mm aaaa: "); scanf("%49[^\n]%d%d%d", c.nome, &c.dia, &c.mes, &c.ano); fseek(file, id * sizeof(Contato), SEEK_SET); fwrite(&c, sizeof(Contato), 1, file); } fclose(file); } else printf("\nErro ao abrir arquivo!\n"); } int main(){ Contato *agenda[50]; char arquivo[] = {"agenda.txt"}; char arquivo2[] = {"agenda.dat"}; int opcao, tam = 50, quant = 0; do{ printf("\n\t0 - Sair\n\t1 - Cadastrar\n\t2 - Alterar\n\t3 - Imprimir\n"); printf("\t4 - Salvar\n\t5 - Ler arquivo\n\t6 - SalvarB\n\t7 - LerB\n\t8 - AltererB\n"); scanf("%d", &opcao); getchar(); switch(opcao){ case 1: quant += cadastrar_contato(agenda, quant, tam); break; case 2: alterar_contato(agenda, quant); break; case 3: imprimir(agenda, quant); break; case 4: salvar(agenda, quant, arquivo); break; case 5: quant = ler_arquivo(agenda, arquivo); break; case 6: salvarB(arquivo2, agenda, quant); break; case 7: quant = lerB(arquivo2, agenda); break; case 8: alterarB(arquivo2); break; default: if(opcao != 0) printf("\n\tOpcao invalida!!!\n"); } }while(opcao != 0); return 0; }