Sort list on in C

Asked

Viewed 1,942 times

2

My problem involves reading data from a text file, where is provided the CPF, name, email, age, sort using list sort in ascending order through age, if you are equal age sort by Cpf. But I’m having trouble sorting the list on, so far I’ve done it

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

typedef struct Dado{
    char CPF[12];
    char nome[41];
    char email[31];
    int idade;
    struct Dado* proximo;
}Dado;

int ler_string_arq(FILE* arq, char *campo_destino){
    int letra = 0;
    char ch;
    if (fscanf(arq,"%c",&ch) == EOF)
    {
        return 1;       
    }
    while( ch != ',') 
    {
        campo_destino[letra] = ch;
        fscanf(arq,"%c", &ch);
        letra++;
    }
    campo_destino[letra] = '\0';
return 0;
}

int main()
{
FILE *arq, *arqout;
char ch,teste;

arq = fopen("read.txt","r");

Dado *inicio = NULL, *fim = NULL;

while (1)
{
    Dado *pessoa = malloc(sizeof(Dado));
    pessoa->proximo = NULL;
    if (inicio == NULL){
        inicio = pessoa;
    }
    if (fim != NULL){

        fim->proximo = pessoa;
    }
    fim = pessoa;

    if (ler_string_arq(arq, pessoa->CPF)){
        free(pessoa);
        break;
    }
    ler_string_arq(arq, pessoa->nome);
    ler_string_arq(arq,pessoa->email);
    fscanf(arq,"%d", &pessoa->idade);
    fscanf(arq,"%c", &ch);
}
fclose(arq);


arqout = fopen("write.txt","w");
Dado* pessoa = inicio;
while (pessoa->idade != 0){
    fprintf(arqout,"%s,%s,%s,%d\n", pessoa->CPF, pessoa->nome, pessoa->email, pessoa->idade);
    pessoa= pessoa->proximo;
}
fclose(arqout);
return 0;
}

I’m starting to study lists, someone could indicate a good way to sort what I’ve done and how that would change the print

1 answer

1


Ordering with copy of values

The simplest way to demonstrate this for the code you have is through an algorithm like the Bubble Sort with the copy of the values of each structure. The difference is mostly in the list, which is done through the pointers proximo.

Implementation:

Dado *pessoa1 = inicio;

while (pessoa1 != NULL){
    Dado *pessoa2 = pessoa1->proximo;
    while (pessoa2 != NULL){
        if (pessoa1->idade > pessoa2->idade){ //se maior troca o conteudo das duas pessoas
            int temp_idade = pessoa1->idade; 
            pessoa1->idade = pessoa2->idade;
            pessoa2->idade = temp_idade;
            troca_string(pessoa1->CPF, pessoa2->CPF, 12);
            troca_string(pessoa1->nome, pessoa2->nome, 41);
            troca_string(pessoa1->email, pessoa2->email, 31);
        }
        pessoa2 = pessoa2->proximo;
    }

    pessoa1 = pessoa1->proximo;
}

The first while scrolls through the list from start to finish, and the second while He’s going to go further and every time he finds a person of older age, he exchanges all of that person’s values. As there are several strings to be exchanged I chose to create a function that switches the string up to a certain number of characters in order to simplify. As there was only one field of the whole type to be exchanged I did no function for that exchange.

The function troca_string would be:

void troca_string(char *str1, char *str2, size_t tamanho){
    static char temp[50];
    memcpy(temp, str1, tamanho);
    memcpy(str1, str2, tamanho);
    memcpy(str2, temp, tamanho);
}

In this case the static prevents the string temp is created every time the function is called, and is created only once, which is ideal for this scenario. To change the string itself I used the function memcpy which already exists to copy the whole string, but could have done the same at the expense of a loop normal copying letter to letter.

This implementation although it works is not very good because the change of each element of the structure is very heavy. In my machine the sizeof(Dado) is 92, which is 92 bytes, which means that each exchange has to copy 92 bytes 3 times. And it can be even worse depending on the amount and size of the structure fields, as well as the amount of elements to sort. The solution to this problem is pointers.

Ordering by pointer exchange

With changing pointers we can avoid copying the various values, but this solution usually implies changing the original structure and consequently much of the code and so it becomes a bit boring.

In this case the structure would have a pointer to the data and another to the next node of the list as it had previously:

typedef struct Info { //informação de cada pessoa
    char CPF[12];
    char nome[41];
    char email[31];
    int idade;
} Info;

typedef struct Dado {
    Info* info; //agora ponteiro em vez de os campos diretamente
    struct Dado* proximo;
} Dado;

The structure Dado actually corresponds to each node in the list whereas the structure Info corresponds to the information of each person.

With this change the reading of the file information would have to be different as not only have to allocate also space for the Info how reading has to become about the Info:

int main() {
    ... 
    Dado *inicio = NULL, *fim = NULL;

    while (1) {
        Dado *pessoa = malloc(sizeof(Dado));
        pessoa->info = malloc(sizeof(Info)); //alocar também o info

        pessoa->proximo = NULL;
        if (inicio == NULL) {
            inicio = pessoa;
        }
        if (fim != NULL) {
            fim->proximo = pessoa;
        }

        if (ler_string_arq(arq, pessoa->info->CPF)) { //agora ->info->CPF
            free(pessoa->info); //liberar tambem o info
            free(pessoa);
            fim->proximo = NULL;
            break;
        }
        fim = pessoa;

        ler_string_arq(arq, pessoa->info->nome); //->info->nome
        ler_string_arq(arq,pessoa->info->email); //->info->email
        fscanf(arq,"%d", &pessoa->info->idade); //->info->idade
        fscanf(arq,"%c", &ch);
    }
    fclose(arq);

Sorting now becomes simpler and more efficient because when it is necessary to change two people of order, just change the pointer to the information of each node:

Dado *pessoa1 = inicio;

while (pessoa1 != NULL){
    Dado *pessoa2 = pessoa1->proximo;
    while (pessoa2 != NULL){
        if (pessoa1->info->idade > pessoa2->info->idade){
            Info *temp = pessoa1->info; //agora troca só o info
            pessoa1->info = pessoa2->info;
            pessoa2->info = temp;
        }
        pessoa2 = pessoa2->proximo;
    }

    pessoa1 = pessoa1->proximo;
}

In the show you also have to change to ->info:

arqout = fopen("write.txt","w");
Dado* pessoa = inicio;
while (pessoa != NULL) {
    fprintf(arqout,"%s,%s,%s,%d\n", pessoa->info->CPF, pessoa->info->nome, pessoa->info->email, pessoa->info->idade);
    pessoa= pessoa->proximo;
}
fclose(arqout);

Complete code for reference:

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

typedef struct Info {
    char CPF[12];
    char nome[41];
    char email[31];
    int idade;
} Info;

typedef struct Dado {
    Info* info;
    struct Dado* proximo;
} Dado;

int ler_string_arq(FILE* arq, char *campo_destino) {
    int letra = 0;
    char ch;
    if (fscanf(arq,"%c",&ch) == EOF) {
        return 1;
    }
    while( ch != ',') {
        campo_destino[letra] = ch;
        fscanf(arq,"%c", &ch);
        letra++;
    }
    campo_destino[letra] = '\0';
    return 0;
}

int main() {
    FILE *arq, *arqout;
    char ch,teste;

    arq = fopen("read.txt","r");

    Dado *inicio = NULL, *fim = NULL;

    while (1) {
        Dado *pessoa = malloc(sizeof(Dado));
        pessoa->info = malloc(sizeof(Info));

        pessoa->proximo = NULL;
        if (inicio == NULL) {
            inicio = pessoa;
        }
        if (fim != NULL) {

            fim->proximo = pessoa;
        }

        if (ler_string_arq(arq, pessoa->info->CPF)) {
            free(pessoa->info);
            free(pessoa);
            fim->proximo = NULL;
            break;
        }
        fim = pessoa;

        ler_string_arq(arq, pessoa->info->nome);
        ler_string_arq(arq,pessoa->info->email);
        fscanf(arq,"%d", &pessoa->info->idade);
        fscanf(arq,"%c", &ch);
    }
    fclose(arq);

    Dado *pessoa1 = inicio;

    while (pessoa1 != NULL){
        Dado *pessoa2 = pessoa1->proximo;
        while (pessoa2 != NULL){
            if (pessoa1->info->idade > pessoa2->info->idade){
                Info *temp = pessoa1->info;
                pessoa1->info = pessoa2->info;
                pessoa2->info = temp;
            }
            pessoa2 = pessoa2->proximo;
        }

        pessoa1 = pessoa1->proximo;
    }

    arqout = fopen("write.txt","w");
    Dado* pessoa = inicio;
    while (pessoa != NULL) {
        fprintf(arqout,"%s,%s,%s,%d\n", pessoa->info->CPF, pessoa->info->nome, pessoa->info->email, pessoa->info->idade);
        pessoa= pessoa->proximo;
    }
    fclose(arqout);
    return 0;
}

Browser other questions tagged

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