File reading in C

5

I'm doing a project in C, which requires creating and reading a file. I'm having trouble implementing the reading. In reading I have already tried to apply fgets() , fscanf() and fread() . But none worked. Does anyone suggest anything to me?

Thank you in advance.

Follow the code:

main()
{
    //Declaração de variáveis

    Cliente *primeiro = NULL;
    int opcao;

    //Artificio para repetir o programa

    while (opcao != 5)
    {
        //Menu de opcoes

        printf("MENU\n");
        printf("<1> Novo cadastro de cliente \n");
        printf("<2> Listar clientes\n");
        printf("<3> Excluir cadastro de cliente\n");
        printf("<4> Alterar cadastrado de cliente\n");
        printf("<5> Sair");

       //lendo a opcao do menu
        fflush(stdin);
        printf("\nDigite a opcao desejada:");
        scanf("%d",&opcao);



    switch(opcao)
    {
        case 1:

        //Inserindo os clientes
        fflush(stdin);
        system("cls");
        printf("NOVO CADASTRO DE CLIENTE\n");
        primeiro = inserir_cliente(primeiro);
        getch();
        system("cls");
        break;

        case 2:
       //Listando os clientes
        system("cls");
        printf("CLIENTES CADASTRADOS\n");
        listar_cliente(primeiro);
        getch();
        system("cls");
        break;

        case 3:
        //excluir cliente
        system("cls");
        printf("EXCLUIR CLIENTES");
        primeiro = excluir_cliente(primeiro);
        getch();
        system("cls");
        break;

        case 4:
        //açterar informaçoes do cliente
        system("cls");
        printf("ALTERAR INFORMACOES DO CLIENTE");
        alterar_cliente(primeiro);
        getch();
        system("cls");
        break;

        case 5:
        //artificio para sair do programa.
        opcao = 5;
        break;

        default:
        system("cls");
        break;

    }
  }
}

Cliente* inserir_cliente(Cliente *primeiro)
{
    FILE *cli;

    cli = fopen("cadastrocliente.txt","a+");

    if(cli == NULL)
    {
        printf("Arquivo NAO pode ser aberto");
        system("pause");
        return 0;
    }

    Cliente novocliente;
    Cliente *atual = primeiro;
    char identificador = 'F';

    //Lendo as informaçoes do cliente.

    printf("\nNome:");
    fflush(stdin);
    fgets(novocliente.nome,40,stdin);
    printf("\n");

    printf("CPF:",135);
    fflush(stdin);
    fgets(novocliente.cpf,15,stdin);
    printf("\n");

    printf("Codigo:",162);
    scanf("%u",&novocliente.codigo);
    printf("\n");


    //Verificando se o cadastro já existe.

    for(atual=primeiro; atual!=NULL; atual=atual->prox)
    {
        if(atual->codigo==novocliente.codigo)
        {
            identificador= 'V';
            break;
        }
    }
    if(identificador!='V' && (strlen(novocliente.nome)!=1 && strlen(novocliente.cpf)!=1))
    {
        //Alocando os espaços e guardando as informaçoes do cliente.
        Cliente* ClienteNovo=(Cliente*) malloc(sizeof(Cliente));
        strcpy(ClienteNovo->nome,novocliente.nome);
        strcpy(ClienteNovo->cpf,novocliente.cpf);
        ClienteNovo->codigo=novocliente.codigo;
        ClienteNovo->prox=primeiro;


        printf("Cadastro realizado com sucesso.");


        fprintf(cli,"%u\n",novocliente.codigo);
        fprintf(cli,"%s",novocliente.nome);
        fprintf(cli,"%s",novocliente.cpf);

        system("cls");

        printf("Codigo: %u",novocliente.codigo);
        printf("\nNome: %s",novocliente.nome);
        printf("CPF: %s",novocliente.cpf);


        getchar();
        fclose(cli);
        //system("cls");

        printf("\n\nPRESSIONE QUALQUER TECLA PARA VOLTAR AO MENU PRINCIPAL.");
        return ClienteNovo;
    }

     else
    {
        printf("Cadastro INVALIDO",160);
    }

}

void listar_cliente(Cliente* primeiro)
{

    FILE *cli;

    cli = fopen("cadastrocliente.txt","r");

    if(cli == NULL)
    {
        printf("Arquivo NAO pode ser aberto.\n");
        system("pause");
        //return 0;
    }



    Cliente* atual; //Ponteiro para percorrer a lista sem perder a referência do primeiro elemeto da lista.


        //Imprimindo os clientes da lista, e suas respectivas informações.


    for(atual = primeiro; atual != NULL; atual = atual -> prox)
    {
        printf("\n Nome:");
        printf("%s", atual -> nome);
        printf("\n CPF:",162);
        printf("%s", atual -> cpf);
        printf("\n Codigo:");
        printf("%u",atual -> codigo);
        printf("\n\n");

    }

    if(primeiro == NULL)
    {
        printf("\n\nNENHUM CLIENTE CADASTRADO.\n");
    }


    fclose(cli);


    printf("\n\nPRESSIONE QUALQUER TECLA PARA VOLTAR AO MENU PRINCIPAL.");
}

Cliente* excluir_cliente(Cliente* primeiro)
{
    FILE *cli;

    cli = fopen("cadastro.txt","a");

     if(cli == NULL)
    {
        printf("Arquivo NAO pode ser aberto");
        system("pause");
        return 0;
    }

     Cliente *anterior=NULL;// ponteiro para saber o elemento anterior ao elemento atual da lista.
     Cliente *atual=primeiro;//Ponteiro para percorrer a lista sem perder o primeiro elemento da lista.
     unsigned long int codigo=0;


     //Requisitando e lendo o código do cliente a ser excluido.
     printf("\nCodigo do cliente a ser excluido:",162,161);
     fflush(stdin);
     scanf("%u",&codigo);

     //Procurando o cliente na lista

     while(atual!= NULL && atual->codigo!=codigo)
     {
         anterior=atual;
         atual=atual->prox;
     }

     //Mensagem caso o cliente não seja encontrado.

     if(atual==NULL)
     {
         printf("\n Cliente NAO encontrado.",198);
         printf("\n\nPRESSIONE QUALQUER TECLA PARA VOLTAR AO MENU PRINCIPAL.");
         return primeiro;
     }

     //Excluindo o primeiro cliente da lista.

     if(anterior==NULL)
     {
         printf("\n Conteudo EXCLUIDO com sucesso.",163,161);
         primeiro=atual->prox;
     }
     //Excluindo um cliente do meio da lista.
     else
     {
         printf("\n Conteudo excluido com sucesso.",163,161);
         anterior->prox=atual->prox;

     }
     //Desalocando o espaço da memoria.
     free(atual);
     printf("\n\nPRESSIONE QUALQUER TECLA PARA VOLTAR AO MENU PRINCIPAL.");
     return primeiro;

     getchar();
    fclose(cli);

}

void alterar_cliente(Cliente* primeiro)
{
     char nome_substituto[40], cpf_substituto[40];
     unsigned long int codigo;
     Cliente* atual=primeiro;

     //Requisitando e lendo o codigo do cliente a ser alterado.

     printf("\n\nCodigo do cliente a ser alterado:",162);
     fflush(stdin);
     scanf("%u",&codigo);

     //Procurando o cliente na lista.
     while(atual!=NULL && atual->codigo!=codigo)
     {
         atual= atual->prox;
     }

     //Alterando os dados do cliente.

     if(atual!=NULL)
     {
         printf("\nNovo Nome:");
         fflush(stdin);
         fgets(nome_substituto,40,stdin);
         strcpy(atual->nome,nome_substituto);

         printf("\nNovo cpf:",135);
         fflush(stdin);
         fgets(cpf_substituto,40,stdin);
         strcpy(atual->cpf,cpf_substituto);

         printf("\n Dados ALTERADOS com sucesso.");

     }else{
         printf("\n\n Cliente NAO Encontrado",198);
     }

     printf("\n\n\nPRESSIONE QUALQUER TECLA PARA VOLTAR AO MENU PRINCIPAL.");
}
    
asked by anonymous 09.11.2015 / 06:44

1 answer

3

Use fread and fwrite . To use them, you must open the file in binary mode .

File size is obtained through fseek followed by ftell .

Here is a way to insert or update the client in the file. If the code does not exist, it inserts it. If it does, it changes:

// Estrutura que mostra como o cliente é salvo no arquivo.
// Isso é diferente da forma como ele é representado em memória por não haver o ponteiro prox.
// Outras diferenças podem vir a ser possíveis também.
typedef struct {
    char nome[41]; // 40 caracteres mais o terminador nulo.
    char cpf[16]; // 15 caracteres mais o terminador nulo.
    int codigo;
} ClienteArquivo;

void inserir_ou_alterar_cliente_no_arquivo(Cliente* cliente) {
    FILE *arquivo = fopen("cadastrocliente.dat", "a+b");

    if (arquivo == NULL) {
        printf("Arquivo NAO pode ser aberto.\n");
        system("pause");
        return;
    }

    fseek(arquivo, 0L, SEEK_END);
    int tamanho_do_arquivo = ftell(fp);
    int numero_de_registros = tamanho_do_arquivo / sizeof(ClienteArquivo);

    ClienteArquivo lido;

    // Percorre o arquivo até achar a posição aonde o cliente está lá.
    // Ao término, i será o número do registro no qual o cliente está,
    // ou então será o fim do arquivo (numero_de_registros) se o cliente
    // não for encontrado.
    int i;
    for (i = 0; i < numero_de_registros; i++) {
        fseek(arquivo, i * sizeof(ClienteArquivo), SEEK_SET);
        fread(&lido, sizeof(ClienteArquivo), 1, arquivo);
        if (lido.codigo == cliente->codigo) break;
    }

    // Copia os dados do cliente.
    memset(&lido, 0, sizeof(ClienteArquivo)); // Limpa para evitar que eventuais lixos na memória acabem sendo escritos no arquivo.
    strcpy(lido.nome, cliente->nome); // Copia o nome.
    lido.nome[40] = 0; // Força o terminador nulo.
    strcpy(lido.cpf, cliente->cpf); // Copia o CPF.
    lido.cpf[15] = 0; // Força o terminador nulo.
    lido.codigo = cliente->codigo; // Copia o código.

    // Se terminou de percorrer o for e não achou no arquivo, então posiciona no final.
    if (i == numero_de_registros) fseek(arquivo, 0L, SEEK_END);

    // Escreve o cliente.
    fwrite(&lido, sizeof(ClienteArquivo), 1, arquivo);

    fclose(arquivo);
}

Deletion is a little more difficult as the size of the file decreases in this case. Also, deleting in the middle of the file would leave holes. The workaround is to copy the last record from the file to the position where the deletion occurred and then make the file size shorter .

void excluir_cliente_no_arquivo(Cliente* cliente) {
    FILE *arquivo = fopen("cadastrocliente.dat", "a+b");

    if (arquivo == NULL) {
        printf("Arquivo NAO pode ser aberto.\n");
        system("pause");
        return;
    }

    fseek(arquivo, 0L, SEEK_END);
    int tamanho_do_arquivo = ftell(fp);

    if (tamanho_do_arquivo != 0) {

        int numero_de_registros = tamanho_do_arquivo / sizeof(ClienteArquivo);

        ClienteArquivo ultimo;

        // Lê o último registro.
        fseek(arquivo, tamanho_do_arquivo - sizeof(ClienteArquivo), SEEK_SET);
        fread(&ultimo, sizeof(ClienteArquivo), 1, arquivo);

        ClienteArquivo lido;

        // Percorre o arquivo até achar a posição aonde o cliente está lá.
        // Ao término, i será o número do registro no qual o cliente está,
        // ou então será o fim do arquivo (numero_de_registros) se o cliente
        // não for encontrado.
        int i;
        for (i = 0; i < numero_de_registros; i++) {
            fseek(arquivo, i * sizeof(ClienteArquivo), SEEK_SET);
            fread(&lido, sizeof(ClienteArquivo), 1, arquivo);
            if (lido.codigo == cliente->codigo) break;
        }

        // Se achou, substitui o registro encontrado com o último e diminui o arquivo.
        if (i < numero_de_registros) {
            fseek(arquivo, i * sizeof(ClienteArquivo), SEEK_SET);
            fwrite(&ultimo, sizeof(ClienteArquivo), 1, arquivo);
            ftruncate(fileno(arquivo), tamanho_do_arquivo - sizeof(ClienteArquivo));
        }
    }
    fclose(arquivo);
}

Finally, you'll also need to fetch a client from the file and return your data:

ClienteArquivo* procurar_cliente_no_arquivo(int codigo) {
    FILE *arquivo = fopen("cadastrocliente.dat", "a+b");

    if (arquivo == NULL) {
        printf("Arquivo NAO pode ser aberto.\n");
        system("pause");
        return NULL;
    }

    ClienteArquivo *lido = (ClienteArquivo *) malloc(sizeof(ClienteArquivo));

    // Percorre o arquivo até achar a posição aonde o cliente está lá.
    // Ao término, i será o número do registro no qual o cliente está,
    // ou então será o fim do arquivo (numero_de_registros) se o cliente
    // não for encontrado.
    int i;
    for (i = 0; i < numero_de_registros; i++) {
        fseek(arquivo, i * sizeof(ClienteArquivo), SEEK_SET);
        fread(lido, sizeof(ClienteArquivo), 1, arquivo);
        if (lido.codigo == codigo) break;
    }

    // Se não encontrou, desaloca a memória alocada e retorna NULL.
    // Se encontrar retorna um ponteiro para a estrutura alocada. 
    if (i == numero_de_registros) {
        free(lido);
        lido = NULL;
    }

    fclose(arquivo);
    return lido;
}

This function above returns a pointer to the client data. You will then need to create a Cliente based on ClienteArquivo returned, since ClienteArquivo has no pointer to the next record. You should also not forget to call free on ClienteArquivo returned when you no longer need it. The function returns NULL if it does not find the client with the search code.

And then you should only manipulate files only through these functions. Nothing to use fopen , fwrite , fread , fprintf or anything so it is not within these three functions above. In this way, you leave the parts that manipulate the files directly isolated from these functions, and prevent them from spreading and complicating the rest of the application. If you do not break this rule, this will make your inserir_cliente , listar_cliente , alterar_cliente and excluir_cliente (much like any others you need to create) functions much simpler and less prone to possible programming errors, instead you will separate the functions that manipulate the file from the functions that manipulate data structures in memory.

Please note: these functions are very sensitive to the file format! If you create or change the cadastrocliente.dat file by any means other than these three functions, you will end up corrupting your file and leaving your program crazy. Whenever you change the format of ClienteArquivo , I recommend destroying the previous file to avoid being able to manipulate records with wrong formats. And since it is now a binary file, not a text file, I even changed the extent of .txt to .dat . Do not try to manipulate this file in Notepad or treat it as text.

Other tips:

  • Beware of fflush(stdin) . Your code will not work on linux! Also, if you're on linux, you'll have to use system("clear"); instead of system("cls"); . See this link response for how to resolve this and read the response comments as well.

  • Do not pass unneeded parameters in printf . Such as for example: printf("CPF:",135); and printf("\nCodigo do cliente a ser excluido:",162,161); , where 135 , 162 and 161 are useless. There are several places where you do this.

  • Use 0 and 1 for false and true, instead of F or V . The main reason is that this is the default form in C and also the code is easier to read because you would simplify if (identificador == 'V') or if (identificador != 'F') to if (identificador) , and would simplify if (identificador != 'V') or if (identificador == 'F') to if (!identificador) .

  • Whenever you use fclose , make sure it is used in all possible paths of the function. In its excluir_cliente function, for example, this did not happen when the client was not found. However, if you manipulate the file only with the three functions above and delete the file manipulation from all other places, this problem will disappear.

09.11.2015 / 09:54