Help with file handling - C language

Asked

Viewed 38 times

0

I’m doing a program that dynamically allocates student structures and registers the grades of diverse students. Until then, everything ok (shows the average, higher and lower grade and the total students when terminating the program), but now I need to do the file processing.

Except that the way I’m doing, I basically write any number plate (regardless of whether you have it or not) and then write it in the text file, but I wish I didn’t have to write it again and take the number plate and note that were registered at the beginning of the program. And then when you hit the read option, just show the already saved information (and don’t re-register).

I’ll leave the complete code, it’s no build error, so if it gets easier to understand, just run.

ps: It is my first contact with file handling, I have to study much more and accept website/video indications as well. But as I’ve been searching for a week and can not solve, I would like help from the most experienced =]

Thanks in advance!

#include <stdio.h>
#include <stdlib.h>


struct Aluno
{
    int matricula;
    int nota;
};

void cadastraMatricula(struct Aluno *alunos, int quantidadeAlunos);
float calcularMediaAlunos(struct Aluno *alunos, int quantidadeAlunos);
void encontrarMaiorEMenorNota(struct Aluno *alunos, int quantidadeAlunos, int *maiorNota_p, int *menorNota_p);
int GravaArquivo(char *arquivo, char *modo, char* texto);
void Leitura(char* arquivo, char* modo);
void BuscaLinha(char * linha);
void Gravacao(char* arquivo, char* modo);
int BuscaDados(FILE* Arq, char* linha);




int main()
{
    char* arquivo = "ArquivoTex.txt";
    char* modo = "a+";

    struct Aluno *alunos = NULL;

    int op = -1, alunosTot = 0, maiorNota = 0, menorNota = 0;


    do
    {
        printf("\nEscolha uma opcao abaixo:");
        printf("\n0 - Encerrar o Programa\n");
        printf("1 - Cadastrar Aluno\n");
        printf("2 - Ler os dados do arquivo\n");

        scanf("%d", &op);

        if (op == 1)
        {
            struct Aluno *aluno_temp_p = (struct Aluno *)realloc(
                                             alunos, (alunosTot + 1) * sizeof(struct Aluno));

            if (aluno_temp_p != NULL)
            {

                cadastraMatricula(aluno_temp_p, alunosTot);

                alunos = aluno_temp_p;
                alunosTot += 1;

            }

        }

        else if (op == 2){
             // Faz UMA gravacao de cada vez
    Gravacao(arquivo,modo);
    // Faz a leitura de TODAS as linha do arquivo
    Leitura(arquivo, modo);


        }

    }


    while (op != 0);

    printf("- Total de Alunos: %d\n", alunosTot);

    encontrarMaiorEMenorNota(alunos, alunosTot, &maiorNota, &menorNota);

    printf("- Menor Nota: %d\n", menorNota);
    printf("- Maior Nota: %d\n", maiorNota);

    printf("- Media Geral: %.2f", calcularMediaAlunos(alunos, alunosTot));
    printf("\n------------------------------\n");



}

void cadastraMatricula(struct Aluno *alunos, int quantidadeAlunos)
{
    printf("\nInforme a matricula: \n");
    scanf("%d", &alunos[quantidadeAlunos].matricula);

    printf("Informe a nota:\n");
    scanf("%d", &alunos[quantidadeAlunos].nota);
}

float calcularMediaAlunos(struct Aluno *alunos, int quantidadeAlunos)
{
    int i = 0, soma = 0;

    if (quantidadeAlunos == 0)
    {
        return 0;
    }

    for (i = 0; i < quantidadeAlunos; i++)
    {
        soma += alunos[i].nota;
    }

    return (float)soma / quantidadeAlunos;
}

void encontrarMaiorEMenorNota(struct Aluno *alunos, int quantidadeAlunos,
                              int *maiorNota_p, int *menorNota_p)
{

    int i = 0;

    if (quantidadeAlunos == 0)
    {
        *maiorNota_p = 0;
        *menorNota_p = 0;
    }
    else
    {
        for (i = 0; i < quantidadeAlunos; i++)
        {

            if (alunos[i].nota > *maiorNota_p)
            {
                *maiorNota_p = alunos[i].nota;
            }

            else if (alunos[i].nota < *menorNota_p)
            {
                *menorNota_p = alunos[i].nota;
            }
        }
    }
}


void Leitura(char* arquivo, char* modo)
{
    FILE* Arq;
    int retorno=99;
    char buffer[1024];

    Arq = fopen(arquivo, modo);
    if (Arq == NULL)
    {
        printf("Erro na abertura do arquivo...");
        return;
    }

    printf("\n");
    while (retorno  != 0)
    {
        retorno = BuscaDados(Arq, buffer);
        if (retorno != NULL)
            printf("\n ---> %s  ", buffer);

    }

    printf("\n");
    fclose(Arq);
}



int GravaArquivo(char *arquivo, char *modo, char* texto)
{
    FILE* Arq;
    int retorno=0;

    Arq = fopen ( arquivo, modo ) ;
    if(Arq == NULL)
     {
        printf("Erro na abertura do arquivo!");
        return (-1);
     }
    retorno = fputs(texto, Arq);
    if (retorno != 0)
    {
        printf("Erro de gravacao!");
        return (-1);
     }

    fclose(Arq);
    return 0;
}

void BuscaLinha(char * aluno)
{
    printf("\nDigite : ");
    scanf("%s",aluno);
}


void Gravacao(char* arquivo, char* modo)
{
    char buffer[1024];
    BuscaLinha(buffer);
    strcat(buffer,"\n");

    if (GravaArquivo(arquivo, modo, buffer) != 0)
    {
       printf("Erro na gravacao do arquivo!");
    }

}

int BuscaDados(FILE* Arq, char* linha)
{
    char buffer[1024];
    linha[0] = '\0';
    int resultado = 0;

    resultado = fgets(linha, sizeof(buffer), Arq);

    return resultado;

}

1 answer

0

ps: It is my first contact with file handling, I have to study much more and accept website/video indications as well. But as I’ve been searching for a week and can not solve, I would like help from the most experienced

Treating files is very simple in C compared to treating memory and addresses.

There are two ways to do this:

  • one with line-oriented functions and supposing that the content will be arranged in records of indeterminate size and separated by a delimiter, the popular line, separated by \n the popular newline

    • the functions used in this case may be fscanf(), fprintf(), fgets(), fputs() and others.
  • the other way assumes the contents arranged in blocks, which can be of a single byte, and the blocks can be treated per unit

    • functions in that case may be fread(), fwrite(), fseek(), ftell() and others.

For C there is no file type and it is just bytes. It is your decision to use one mode or another. There is no text file or binary file. There is only file. You can read so-called binary files as text and vice versa, and the results vary from normal to unexpected disaster.

An example

The following program creates a file like binary and reads how text. It is useless. It serves only to show some of the functions and the little relevance of the idea of "file type".

#include <stdio.h>

int main(void)
{
    const char* arquivo = "coisa.txt";
    FILE*       dados   = fopen(arquivo, "w");
    if (dados == NULL) return -1;

    char   bloco[]   = {"12345678123456781234567812345678"};
    size_t tam_bloco = 8;
    size_t n_blocos  = 4;
    size_t res       = fwrite(bloco, tam_bloco, n_blocos, dados);
    printf("fwrite() retornou %zd\n", res);
    if (res == n_blocos)
        printf("fwrite() gravados %zd blocos de %zd bytes\n", n_blocos,
               tam_bloco);

    char val = 0;
    res      = fwrite(&val, sizeof(val), 1, dados);
    printf("fwrite() retornou %zd\n", res);
    if (res == sizeof(val))
        printf("fwrite() gravados %zd blocos de %zd bytes\n", (size_t)1,
               (size_t)sizeof(val));

    size_t tam_arquivo = ftell(dados);
    printf("ftell() reporta %zd como tamanho do arquivo '%s'\n",
           tam_arquivo, arquivo);
    fclose(dados);

    dados = fopen(arquivo, "r");
    printf("arquivo \"%s\" reaberto\n", arquivo);
    if (dados == NULL) return -2;
    char  linha[200];
    char* ret = fgets(linha, sizeof(linha), dados);
    if (ret == NULL) return -3;
    printf("fgets() retornou ok e leu \"%s\" do arquivo\n", linha);

    printf("Tentando ler outra linha...\n");
    ret = fgets(linha, sizeof(linha), dados);
    if (ret == NULL)
    {
        printf("fgets() retornou NULL\n");
        if (feof(dados)) printf("feof() indica fim de arquivo\n");
    }
    fclose(dados);
}

example output

fwrite() retornou 4
fwrite() gravados 4 blocos de 8 bytes
fwrite() retornou 1
fwrite() gravados 1 blocos de 1 bytes
ftell() reporta 33 como tamanho do arquivo 'coisa.txt'
arquivo "coisa.txt" reaberto
fgets() retornou ok e leu "12345678123456781234567812345678" do arquivo
Tentando ler outra linha...
fgets() retornou NULL
feof() indica fim de arquivo

Back to your show

int BuscaDados(FILE* Arq, char* linha)
{
    char buffer[1024];
    linha[0]      = '\0';
    int resultado = 0;
    resultado = fgets(linha, sizeof(buffer), Arq);

    return resultado;
}

You have a particular problem here: fgets() returns char* a pointer to linha in your case. Does not return int. Look at the example I showed you above.

Write around the data

Your data

struct Aluno
{
    int matricula;
    int nota;
};

// ...

    struct Aluno* alunos = NULL;

And your idea is to treat alunos as a vector of struct Aluno and use realloc() to each new student to resize the structure. I’ve seen it a lot in student programs but it’s not a good idea. It’s fragile and very inefficient.

Here’s what happens: every realloc() the system tries to find an area with the capacity to contain the new size, and possibly copies what it already has to the new area. There is no guarantee that it will increase but continue in the same place. And these copies may take time. And they are expendable. Even more: one area will be released and another will be created. Then the data will be copied and the pointer will be reassigned to the new address. If something fails in the path depending on the implementation one can lose the two addresses and all the data. So first it is called realloc() with a temporary pointer and if it worked there then copy the address to the original pointer. You did so with aluno_temp_p.

But the common thing is to allocate a larger amount of records each time, like 10 or 100 students. And as you use it, you allocate more equal blocks. No constant copies, less risk. Faster. Only realoca when it runs out, and using a reasonable block becomes simpler.

the functions

void  cadastraMatricula(struct Aluno* alunos, int quantidadeAlunos);
float calcularMediaAlunos(struct Aluno* alunos, int quantidadeAlunos);
void  encontrarMaiorEMenorNota(struct Aluno* alunos,
                               int quantidadeAlunos, int* maiorNota_p,
                               int* menorNota_p);
int   GravaArquivo(char* arquivo, char* modo, char* texto);
void  Leitura(char* arquivo, char* modo);
void  BuscaLinha(char* linha);
void  Gravacao(char* arquivo, char* modo);
int   BuscaDados(FILE* Arq, char* linha);

Note that you have to keep controlling the number of students separately and move into the functions and everything gets complicated: imagine if I had 4 classes of students to control in the program

One more level of composition


typedef struct
{
    int matricula;
    int nota;

} Aluno;

typedef struct
{
    unsigned qtd;         // quantos tem agora
    unsigned bloco;       // alocados bloco a bloco
    unsigned capacidade;  // quantos tem agora alocados
    Aluno*   al;          // o vetor

} Alunos;

void  cadastraMatricula(Alunos* alunos);
float calcularMediaAlunos(Alunos* alunos);
void  encontrarMaiorEMenorNota(
    Alunos* alunos, int* maiorNota_p, int* menorNota_p);

int  GravaArquivo(char* arquivo, char* modo, char* texto);
void Leitura(char* arquivo, char* modo);
void BuscaLinha(char* linha);
void Gravacao(char* arquivo, char* modo);
int  BuscaDados(FILE* Arq, char* linha);

Understand that if you use like this everything becomes easier: Alunos is your data set. Within each Alunos has the amount, and can set there the allocation of guys, in blocks, if you prefer. That’s the notion of encapsulation.

Further, if you suspect that you will need often the highest and lowest note and the average can leave it in the structure itself, saving a lot of time and loops in exchange for a few int:


    unsigned soma; // para calcular a media
    int      maior; // maior e menor nota
    int      menor;

About the archives

For its use it is simpler to record and read the disk records in terms of the structure of Aluno at the beginning and end of the execution, and during the use of the program use everything in memory. It is only an exercise after all. And to speed things up I think a simple index vector for the enrollments may be the solution: keep an updated vector with the enrollments and the position of the student in the vector and so you do not need to enter in order ;)

The index can be inside the structure or in a structure the part in the program, is only a vector of int after all. As there will be millions of records this gives a good performance with good use of cache and without the stress of keeping in order... Searching the vector is almost instantaneous.

I’m not going to write a whole program now, but to have an example of what it would be like, consider

Example

This program declares and uses a few classes, in a common way of maintaining that kind of structure. If you decide to go down a path like this and have difficulty I can implement the rest

#include <stdio.h>
#include "alunos.h"

int main(void)
{
    Alunos* turma_1 = cria_turma(30); // blocos de 30 
    Alunos classe[10]; // 10 grupos de alunos
    printf("Turma 1 tem %d alunos\n", turma_1->qtd);
    turma_1 = apaga_turma(turma_1);
    return 0;
}

Note that it is easy for example to have a vector of classes of students, which would be almost unthinkable using only one pupil vector for each class, and separate counters.

example output

Turma 1 tem 0 alunos
  • delet_class()` returns a pointer so it can be used to invalidate the deleted class pointer, on the same line and at the same time.

creating and erasing a class

#include <stdlib.h>
#include "alunos.h"

Alunos* apaga_turma(Alunos* T)
{
    free(T->index);  // apaga o indice
    free(T->al);     // os alunos
    free(T);         // e apaga a estrutura
    return NULL;
}

Alunos* cria_turma(unsigned N)
{
    Alunos* novo = (Alunos*) malloc(sizeof(Alunos));
    if ( novo == NULL ) return NULL;

    novo->qtd= 0;
    novo->bloco = N;
    novo->capacidade = N;

    novo->soma = 0;
    novo->maior = INT_MIN;
    novo->menor = INT_MAX;

    novo->al = (Aluno*) malloc(N * sizeof(Aluno));
    novo->index = (int*) malloc(sizeof(int));
    
    return novo;
}

With everything located INSIDE the functions becomes easier to control.

Note that the class already starts empty, but the first block of pointers is already created, and the index vector.

Just a hunch.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.