Jump to content

Listas ligadas


Go to solution Solved by yhara,

Recommended Posts

Boa noite a todos,

Estou a tentar aprender C sozinha e até me tenho safado razoavelmente para quem nunca tinha programado.

Estava a correr bem até chegar ás listas. Se for uma lista simples , só com uma struct, consigo fazer algumas coisinhas, mas juntar duas structs é que a coisa complica, ainda para mais quando não encontro muita informação disponível.

Se a inserção dos dados for diretamente pelo utilizador consigo fazer mas, ler de um  ficheiro é que não estou a conseguir fazer.

Falando agora do programa: 

Leio através de um ficheiro .txt , cada linha é composto por produtos e a seguir a esse produto as linhas seguintes são compostas por datas e um preço.

Ex:

Produto B

2010/01/01 2.49

2012/01/01 2.59

Produto A

2012/01/01 1.74

2010/01/01 1.49

Produto C

2010/01/01 0.49

2014/01/01 0.99

2012/01/01 0.89

Produto D

2014/01/01 3.99

2014/04/01 4.09

2014/04/15 4.19

2014/04/25 4.29

2010/01/01 3.49

2012/01/01 3.89

Se for só considerar os produtos, consigo inserir, ordenar e afins. Mas se juntar a data/preço quando faço print a coisa vem completamente á toa.

O meu Output :

Produtos:

- Produto A;

- 2012/1/1 1.74;

- 2010/1/1 1.49;

- 2010/1/1 0.49;

- 2014/1/1 0.99;

- 2012/1/1 0.89;

- 2014/1/1 3.99;

- 2014/4/1 4.09;

- 2014/4/15 4.19;

- 2014/4/25 4.29;

- 2010/1/1 3.49;

- 2012/1/1 3.89;

- Produto B;

- 2010/1/1 2.49;

- 2012/1/1 2.59;

- Produto C;

- Produto D;

Este é o meu código, não levem a mal todos os comentários, mas como estou a aprender (sozinha), tento comentar tudo para depois perceber onde posso melhorar.

EDIT:"Eu acho que o problema estará que eu não estou a conseguir guardar/passar a informação do ultimo produto inserido para inserir os preços ...também não estou a ver como o conseguir fazer."

Podem por favor me ajudar?

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#include <ctype.h>

#pragma warning(disable : 4996)

#define MAXSTR 255
/*
//--------------------------------------------------------
// define a estrutura da lista da data
//
typedef struct SListData
{
    int ano, mes, dia;
}TListaData;
*/
  
//--------------------------------------------------------
// define a estrutura da lista dos preços
//
typedef struct SListPreco
{
    float preco;
    //TListaData *data;
    int ano, mes, dia;
    struct SListPreco *seg;
}TListaPreco;


//--------------------------------------------------------
// define a estrutura da lista dos produtos
//
typedef struct SListProduto
{
    char* nome;
    TListaPreco *preco;
    struct SListProduto *seg;
}TListaProduto;


//--------------------------------------------------------
// Funcao que adiciona um elemento do inicio da lista.
//
TListaProduto* Adiciona(TListaProduto* lista, char* nome)
{
    //aloca memoria para a lista
    TListaProduto* elemento = (TListaProduto*)malloc(sizeof(TListaProduto));

    //se alocação de memoria não foi bem sucedida
    if (elemento == NULL)
    {
        printf("Erro de alocacao\n");
        return 0;
    }

    //alocar a memoria (tamnho da str + '\0')
    elemento->nome = (char*)malloc(strlen(nome) + 1);

    strcpy(elemento->nome, nome);
    elemento->preco = NULL;///////
    elemento->seg = NULL; // se o seguinte aponta para NULL é porque é a cabeça da lista

     elemento->seg = lista;// o novo elemento fica a apontar para a cabeça da lista 
     //(o primeiro elemento da lista que agora vai passar a ser o segundo)
     lista = elemento; //a cabeça da lista fica a apontar para o novo elemento
     //e como resultado disto o novo elemento fica a apontar para o segundo elemento da lista 
    
    return lista;// devolver sempre a cabeça da lista (estava elemento)
}

//--------------------------------------------------------
// Funcao que remove o primeiro elemento da lista.
//  
TListaProduto* Remove(TListaProduto* lista)
{
    if (lista != NULL)
    {
        TListaProduto* aux = lista->seg;//usar lista auxiliar para não estragar a principal
        free(lista->nome);
        free(lista);
        return aux;//retorna a lista sem o elemento eliminado (primeiro elemento)
    }
    return NULL; //caso o if não corra bem retorna NULL
}

//--------------------------------------------------------
// Funcao que insere elementos à lista.
// ####NOVA####
TListaProduto *Insere(TListaProduto* lista, char* nome)
{
   
    // verifica se o primeiro caracter é uma letra
   // if (isalpha(nome[0]))
   // {
        //strcmp:A função strcmp compara duas strings e devolve um valor inteiro 
        //que lhe diz qual das strings vem antes no código ASCII
        //Um valor maior que zero significa que string1 é maior que string2.
        //Se string1 é "ABC" e string2 é "DEF", strcmp fornece um valor negativo, indicando que string1 é menor que string2.
        if (lista == NULL || (strcmp(lista->nome, nome) > 0))// se vier depois
        {
            return Adiciona(lista, nome);
        }
        lista->seg = Insere(lista->seg, nome);
    //}
    //else
       // printf("erro");
    return lista;    
}

//--------------------------------------------------------
// Funcao que ordena a lista inserindo os elementos dr forma ordenada.
// ####NOVA####
TListaProduto *InsertSort(TListaProduto *lista)
{
    //cria lista vazia onde se vai colocando os elementos
    TListaProduto* aux = NULL;
    while (lista != NULL)
    {
        //inserir o novo elemento
        aux = Insere(aux, lista->nome);
        //apagar o elemento da lista antiga
        lista = Remove(lista);
    }
    //retonrar a nova lista, a antiga já não existe
    return aux;
}
//--------------------------------------------------------
//  Funcao que adiciona um preço ao inicio da lista.
//  ####NOVA####
TListaPreco *AdicionaPreco(TListaPreco* lista, float preco, int ano, int mes, int dia)
{
/*
ultimoproduto=null
ler
---se for produto
------ultimoproduto = adicionaProduto
---se for data
------adiciona data e preço ao ultimoproduto
---proximo*/

//aloca memoria para a lista
    TListaPreco* elemento = (TListaPreco*)malloc(sizeof(TListaPreco));

    //se alocação de memoria não foi bem sucedida
    if (elemento == NULL)
    {
        printf("Erro de alocacao\n");
        return 0;
    }

    elemento->ano = ano;
    elemento->mes = mes;
    elemento->dia = dia;
    elemento->preco = preco;
   
    elemento->seg = NULL; // se o seguinte aponta para NULL é porque é a cabeça da lista


    elemento->seg = lista;// o novo elemento fica a apontar para a cabeça da lista 
    //(o primeiro elemento da lista que agora vai passar a ser o segundo)
    lista = elemento; //a cabeça da lista fica a apontar para o novo elemento
    //e como resultado disto o novo elemento fica a apontar para o segundo elemento da lista 
    
    return lista;// devolver sempre a cabeça da lista (estava elemento)

}

//--------------------------------------------------------
// Funcao que insere preços à lista.
// ####NOVA####
TListaPreco* InserePreco(TListaPreco* lista, float preco, int ano, int mes, int dia)
{

    // verifica se o primeiro caracter é uma letra
   // if (isalpha(nome[0]))
   // {
        //strcmp:A função strcmp compara duas strings e devolve um valor inteiro 
        //que lhe diz qual das strings vem antes no código ASCII
        //Um valor maior que zero significa que string1 é maior que string2.
        //Se string1 é "ABC" e string2 é "DEF", strcmp fornece um valor negativo, indicando que string1 é menor que string2.
    if (lista == NULL)
    {
        return AdicionaPreco(lista, preco, ano, mes, dia);
    }
    lista->seg = InserePreco(lista->seg, preco, ano, mes, dia);

    //}
    //else
       // printf("erro");
    return lista;
}

//--------------------------------------------------------
// Funcao que inverte a ordem dos elementos da lista.
// ####Não está a ser usada agora#####
TListaProduto *Inverte(TListaProduto* lista)
{
    //lista auxiliar
    TListaProduto* aux = NULL;

    while (lista != NULL)
    {
        aux = Adiciona(aux, lista->nome);
        lista = Remove(lista);
    }
    lista = aux;
    return lista;
}

//--------------------------------------------------------
// Funcao que imprime toda a lista.
//  
void Imprime(TListaProduto *lista)
//void Imprime(ListaProduto* aux)
{
    TListaProduto* atual = lista;
    //ListaProduto* atual = aux;
    //TListaPreco* precoAtual = NULL;  // apontador preço auxiliar para navegar nos preços 
    TListaPreco* precoAtual;
    if (atual == NULL)
    {
        //printf("--- fim da lista ---\n\n");
        return;
    }

    while (atual != NULL)
    {
        printf("- %s;\n", atual->nome);
        precoAtual = atual->preco;   // precoAtual fica com o primeiro preço da lista de produtos atual
        while (precoAtual != NULL) 
        {
            printf("- %d/%d/%d %.2f;\n", precoAtual->ano, precoAtual->mes, precoAtual->dia, precoAtual->preco);
            precoAtual = precoAtual->seg;
        }
        atual = atual->seg;// passa para o elemento seguinte
    }
}

//--------------------------------------------------------
// Funcao que imprime toda a lista de preços.
//  
void ImprimePreco(TListaPreco* lista)
//void Imprime(ListaProduto* aux)
{
    TListaPreco* atual = lista;
    //ListaProduto* atual = aux;

    if (atual == NULL)
    {
        //printf("--- fim da lista ---\n\n");
        return;
    }

    while (atual != NULL)
    {
        printf("- %d/%d/%d %.2f;\n", atual->ano, atual->mes, atual->dia, atual->preco);
        atual = atual->seg;// passa para o elemento seguinte
    }
}
//--------------------------------------------------------
// Funcao para libertar a memoria de toda a lista.
//
TListaProduto *Libertar(TListaProduto *lista)  // alterado
{
    TListaProduto *aux;
    while (lista != NULL)
    {
        aux = lista->seg;
        free(lista->nome); //libertar a str
        free(lista);    //libertar a lista
        lista = aux;
    }
    return lista;// retorna a lista vazia
}

//--------------------------------------------------
int main()
{
    //necessario ter sempre as variaveis inicializadas senão dá segmentation fault
    //problemas de memoria
    TListaProduto *listaProduto = NULL;
    TListaPreco* listaPreco = NULL;
    char nome[MAXSTR];

    char* procura, * novaPalavra;
    char datapreco[MAXSTR][MAXSTR];
    char delimitador[] = " /";
    int i = 0;
    int dia = 0;
    int mes = 0;
    int ano = 0;
    float preco = 0;

    nome[0] = '\0'; // para não dar erros no debug

    // Declaramos um ponteiro(link para o endereço da memória) para o arquivo de nome: 'f'
    FILE* f;

    //abrir o ficheiro in.txt em mode de leitura
    //f=fopen("in.txt", "rt");

    //para testar no HR
    f = stdin; //stdin também é um ficheiro

    if (f == NULL)// se o ficheiro não foi aberto
    {
        printf("Erro ao abrir ficheiro");
    }

    while (!feof(f))
    {
        if (fgets(nome, MAXSTR, f) != NULL)
        {
            //como o ficheiro tem novas linhas o printf fica disforme
            //retirar '\n' e colocar o '\0' na ultima posicao
            if (nome[strlen(nome) - 1] == '\n')
            {
                nome[strlen(nome) - 1] = '\0';
            }
            // verifica se o primeiro caracter é uma letra
            if (isalpha(nome[0]))
            {
                listaProduto = Insere(listaProduto, nome);
                //lista = Adiciona(lista, nome);
            }
            else if (isdigit(nome[0]))
            {
                //procura pelo primeiro caracter delimitador
                procura = strtok(nome, delimitador);

                // se encontrou
                while (procura != NULL)
                {
                    //guarda o apontador para depois guardar no strBackup se for preciso
                    novaPalavra = procura;
                    while (procura[0] != 0)
                    {
                        procura++;
                    }
                    //guardar palavra actual
                    strcpy(datapreco[i], novaPalavra);
                    //printf("%s\n", strBackup[i]);
                    i++;

                    //procura o proximo  caracter delimitador
                    procura = strtok(NULL, delimitador);
                }
                i = 0;
                //guardar as variaveis referentes ao ano/mes/dia preço
                ano = atoi(datapreco[0]);
                mes = atoi(datapreco[1]);
                dia = atoi(datapreco[2]);
                preco = atof(datapreco[3]);//converter de char para float

                //listaPreco = InserePreco(listaPreco, preco, ano, mes, dia);
                listaProduto->preco = InserePreco(listaProduto->preco, preco, ano, mes, dia);
            }
            else 
                printf("erro");            
        }
    }
    printf("Produtos:\n");
    //imprime lista
    Imprime(listaProduto);

    //ImprimePreco(listaPreco);

    //liberta a memoria
    listaProduto = Libertar(listaProduto); //como a função não está void tem que ser declarada assim

    return 0;
}
Link to post
Share on other sites
  • Solution

Consegui resolver o problema 🙂

vou deixar o código aqui caso alguém tenha o mesmo problema que eu, o código está pronto para imprimir as listas encadeadas e também para imprimir com a contagem dos preços :

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#include <ctype.h>
#define MAXSTR 255

//--------------------------------------------------------
// define a estrutura da lista da data
//
typedef struct SListData
{
    int ano, mes, dia;
}TListaData;

//--------------------------------------------------------
// define a estrutura da lista dos preços
//
typedef struct SListPreco
{
    float preco;
    TListaData data;// não precisa de ser apontador visto o estrutura não ter apontadores e ser estrutura simples (int)
    int ano, mes, dia;
    struct SListPreco *seg;
}TListaPreco;

//--------------------------------------------------------
// define a estrutura da lista dos produtos
//
typedef struct SListProduto
{
    char* nome;
    TListaPreco *preco;
    struct SListProduto *seg;
}TListaProduto;

//--------------------------------------------------------
// Funcao que adiciona um elemento do inicio da lista.
//
TListaProduto* Adiciona(TListaProduto* lista, char* nome)
{
    //aloca memoria para a lista
    TListaProduto* elemento = (TListaProduto*)malloc(sizeof(TListaProduto));

    //se alocação de memoria não foi bem sucedida
    if (elemento == NULL)
    {
        printf("Erro de alocacao\n");
        return 0;
    }

    //alocar a memoria (tamnho da str + '\0')
    elemento->nome = (char*)malloc(strlen(nome) + 1);

    strcpy(elemento->nome, nome);
    elemento->preco = NULL;///////
    elemento->seg = NULL; // se o seguinte aponta para NULL é porque é a cabeça da lista

     elemento->seg = lista;// o novo elemento fica a apontar para a cabeça da lista 
     //(o primeiro elemento da lista que agora vai passar a ser o segundo)
     lista = elemento; //a cabeça da lista fica a apontar para o novo elemento
     //e como resultado disto o novo elemento fica a apontar para o segundo elemento da lista 
    
    return lista;// devolver sempre a cabeça da lista (estava elemento)
}

//--------------------------------------------------------
// Funcao que remove o primeiro elemento da lista.
//  ####Não está a ser usada####
TListaProduto* Remove(TListaProduto* lista)
{
    if (lista != NULL)
    {
        TListaProduto* aux = lista->seg;//usar lista auxiliar para não estragar a principal
        free(lista->nome);
        free(lista);
        return aux;//retorna a lista sem o elemento eliminado (primeiro elemento)
    }
    return NULL; //caso o if não corra bem retorna NULL
}

//--------------------------------------------------------
// Funcao que insere elementos à lista.
// 
TListaProduto *Insere(TListaProduto* lista, char* nome)
{
        //strcmp:A função strcmp compara duas strings e devolve um valor inteiro 
        //que lhe diz qual das strings vem antes no código ASCII
        //Um valor maior que zero significa que string1 é maior que string2.
        //Se string1 é "ABC" e string2 é "DEF", strcmp fornece um valor negativo, indicando que string1 é menor que string2.
        if (lista == NULL || (strcmp(lista->nome, nome) > 0))// se vier depois
        {
            return Adiciona(lista, nome);
        }
        lista->seg = Insere(lista->seg, nome);

    return lista;    
}

//--------------------------------------------------------
// Funcao que ordena a lista inserindo os elementos de forma ordenada.
// ####Não está a ser usada####
TListaProduto *InsertSort(TListaProduto *lista)
{
    //cria lista vazia onde se vai colocando os elementos
    TListaProduto* aux = NULL;
    while (lista != NULL)
    {
        //inserir o novo elemento
        aux = Insere(aux, lista->nome);
        //apagar o elemento da lista antiga
        lista = Remove(lista);
    }
    //retonrar a nova lista, a antiga já não existe
    return aux;
}
//--------------------------------------------------------
//  Funcao que adiciona um preço ao inicio da lista.
//  
TListaPreco *AdicionaPreco(TListaPreco* lista, float preco, int ano, int mes, int dia)
{
    //aloca memoria para a lista
    TListaPreco* elemento = (TListaPreco*)malloc(sizeof(TListaPreco));

    //se alocação de memoria não foi bem sucedida
    if (elemento == NULL)
    {
        printf("Erro de alocacao\n");
        return 0;
    }

    elemento->data.ano = ano;
    elemento->data.mes = mes;
    elemento->data.dia = dia;
    elemento->preco = preco;
   
    elemento->seg = NULL; // se o seguinte aponta para NULL é porque é a cabeça da lista

    elemento->seg = lista;// o novo elemento fica a apontar para a cabeça da lista 
    //(o primeiro elemento da lista que agora vai passar a ser o segundo)
    lista = elemento; //a cabeça da lista fica a apontar para o novo elemento
    //e como resultado disto o novo elemento fica a apontar para o segundo elemento da lista 
    
    return lista;// devolver sempre a cabeça da lista (estava elemento)
}

//--------------------------------------------------------
// Funcao que insere 1 preço numa lista de preços.
//analogia: escreve numa folha os preços um a um
//
TListaPreco* InserePreco(TListaPreco* lista, float preco, int ano, int mes, int dia)
{    
    if (lista == NULL)
    {
        return AdicionaPreco(lista, preco, ano, mes, dia);
    }
    lista->seg = InserePreco(lista->seg, preco, ano, mes, dia);

    return lista;
}

//--------------------------------------------------------
// Funcao que insere preços à lista.
// ####NOVA####    Função By PS   #####

//a função recebe a lista dos produtos, o ultimo nome que inserimos 
//e a cabeça da lista dos preços por valor
//enquanto a lista não chega ao fim vai procurando o produto, correndo a lista(lista = lista->seg)
//quando encontra e se a lista não tiver chegado ao fim(não encontrou)....insere os preços
// analogia: tem uma folha de preços preenchida e procura o separador certo para inserir essa folha
void InserePRECOS(TListaProduto* lista, char* nome, TListaPreco* precos)
{
    while (lista != NULL && (strcmp(lista->nome, nome) != 0))
    {
        lista = lista->seg;
    }
    if (lista != NULL) {
        lista->preco = precos;
    }
}

//--------------------------------------------------------
// Funcao que inverte a ordem dos elementos da lista.
// ####Não está a ser usada agora#####
TListaProduto *Inverte(TListaProduto* lista)
{
    //lista auxiliar
    TListaProduto* aux = NULL;

    while (lista != NULL)
    {
        aux = Adiciona(aux, lista->nome);
        lista = Remove(lista);
    }
    lista = aux;
    return lista;
}

//--------------------------------------------------------
// Funcao para contar os preços da lista.
// 
int ContaPrecos(TListaProduto* lista)
{
    int contagem = 0;
    TListaPreco* precos = lista->preco;

    precos = lista->preco;
    while (precos != NULL)
    {
        precos = precos->seg;
        contagem++;
    }
    return contagem;
}

//--------------------------------------------------------
// Funcao que imprime lista com contagem dos preços.
//  
void Imprime(TListaProduto* lista)
{
    if (lista == NULL)
    {
        //printf("--- fim da lista ---\n\n");
        return;
    }

    while (lista != NULL)
    {
        printf("- %s", lista->nome);
        printf(" #%d;\n", ContaPrecos(lista));
        lista = lista->seg;// passa para o elemento seguinte
    }
}

//--------------------------------------------------------
// Funcao que imprime toda a lista.
//  
void ImprimeLista(TListaProduto *lista)
//void Imprime(ListaProduto* aux)
{
    TListaProduto* atual = lista;
    TListaPreco* precoAtual;

    if (atual == NULL)
    {
        //printf("--- fim da lista ---\n\n");
        return;
    }

    while (atual != NULL)
    {
        printf("- %s;\n", atual->nome);
        precoAtual = atual->preco;   // precoActual fica com o primeiro preço da lista de produtos actual
        while (precoAtual != NULL) 
        {
            printf("- %d/%d/%d %.2f;\n", precoAtual->ano, precoAtual->mes, precoAtual->dia, precoAtual->preco);
            precoAtual = precoAtual->seg;
        }
        atual = atual->seg;// passa para o elemento seguinte
    }
}

//--------------------------------------------------------
// Funcao para libertar a memoria da listas dos preços.
// 
TListaPreco* LibertarPrecos(TListaPreco* lista)
{
    TListaPreco* precos;
    while (lista != NULL)
    {
        precos = lista->seg;
        free(lista);    //libertar a lista
        lista = precos;
    }
    return lista;// retorna a lista vazia
}

//--------------------------------------------------------
// Funcao para libertar a memoria de toda a lista dos produtos.
//
TListaProduto *LibertarProduto(TListaProduto *lista)
{
    TListaProduto *produto;
    TListaPreco* precos;
    while (lista != NULL)
    {
        produto = lista->seg;
        //porque não usar assim:precos = LibertarPrecos(preco)?:
        //Porque a lista de preços de cada produto está no campo lista->preços. 
        //O campo preços é local e não serve para nada a não ser porque a função libertar devolve um valor
        precos = LibertarPrecos(lista->preco);
        free(lista->nome); //libertar a str
        free(lista);    //libertar a lista
        lista = produto;
    }
    return lista;// retorna a lista vazia
}

//--------------------------------------------------
int main()
{
    //necessario ter sempre as variaveis inicializadas senão dá segmentation fault
    //problemas de memoria
    TListaProduto* listaProduto = NULL;
    TListaPreco* listaPreco = NULL;
    char nome[MAXSTR];
    char nomeAnterior[MAXSTR];

    char* procura, * novaPalavra;
    char datapreco[MAXSTR][MAXSTR];
    char delimitador[] = " /";
    int i = 0;
    int dia = 0;
    int mes = 0;
    int ano = 0;
    float preco = 0;

    // Declaramos um ponteiro(link para o endereço da memória) para o arquivo de nome: 'f'
    FILE* f;

    //abrir o ficheiro in.txt em mode de leitura
    //f=fopen("in.txt", "rt");

    //para testar no HR
    f = stdin; //stdin também é um ficheiro

    if (f == NULL)// se o ficheiro não foi aberto
    {
        printf("Erro ao abrir ficheiro");
    }

    while (!feof(f))
    {

        if (fgets(nome, MAXSTR, f) != NULL)
        {
            //como o ficheiro tem novas linhas o printf fica disforme
            //retirar '\n' e colocar o '\0' na ultima posicao
            if (nome[strlen(nome) - 1] == '\n')
            {
                nome[strlen(nome) - 1] = '\0';
            }
            if (isdigit(nome[0]))
            {
                //procura pelo primeiro caracter delimitador
                procura = strtok(nome, delimitador);

                // se encontrou
                while (procura != NULL)
                {
                    //guarda o apontador para depois guardar no strBackup se for preciso
                    novaPalavra = procura;
                    while (procura[0] != 0)
                    {
                        procura++;
                    }
                    //guardar palavra actual
                    strcpy(datapreco[i], novaPalavra);
                    //printf("%s\n", strBackup[i]);
                    i++;

                    //procura o proximo  caracter delimitador
                    procura = strtok(NULL, delimitador);
                }
                i = 0;
                //guardar as variaveis referentes ao ano/mes/dia preço
                ano = atoi(datapreco[0]);
                mes = atoi(datapreco[1]);
                dia = atoi(datapreco[2]);
                preco = atof(datapreco[3]);//converter de char para float

                //insere 1 preço numa lista de preços
                //analogia: escreve numa folha os preços um a um
                listaPreco = InserePreco(listaPreco, preco, ano, mes, dia);

                if (listaProduto != NULL) // há produto anterior
                {
                    //procura a ultima posição do produto inserido para saber onde inserir os preços
                    //analogia: procura o separador certo (produto) para inserir a folha dos preços
                    InserePRECOS(listaProduto, nomeAnterior, listaPreco);
                }
            }
            // verifica se o primeiro caracter é uma letra
            //inicialmente esta era a primeira verificação if (isalpha(nome[0])), 
            //mas devido aos acentos tive que trocar a ordem dos if e retirar isalpha(nome[0])
            else //(isalpha(nome[0]))
            {
                /*
                // também funciona aqui
                if (listaProduto != NULL) // há produto anterior //**
                {
                    InserePRECOS(listaProduto, nomeAnterior, listaPreco); //**
                }
                    
                listaPreco = NULL; //**
                */
                listaPreco = NULL; // como a lista de produtos é nova a lista de preços começa vazia
                strcpy(nomeAnterior, nome); // guarda o nome do produto para depois se saber a sua posição na lista

                //Insere os produtos a lista
                listaProduto = Insere(listaProduto, nome);
                //lista = Adiciona(lista, nome);
            }
        }
    }
    
    //fazer teste se a lista foi preenchida com sucesso
    if (listaProduto != NULL) 
    {
        printf("Produtos:\n");
        //imprime lista cpmpleta
        //ImprimeLista(listaProduto);

        //imprime lista dos produtos com contagem dos preços
        Imprime(listaProduto);

        //liberta a memoria
        listaProduto = LibertarProduto(listaProduto); //como a função não está void tem que ser declarada assim
    } 
    else 
    {
        printf("erro ficheiro sem produtos fora\n");
    }

    return 0;
}
Link to post
Share on other sites
rbsrbs_dev

Olá yhara

Como disseste que estavas a aprender C sozinho o que normalmente é um tanto complicado, deixo-te algumas observações extra à tua segunda versão. Removi os teus comentários para ser mais fácil leres as anotações:

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#include <ctype.h>

#define MAXSTR 255

typedef struct SListData
{
	int ano, mes, dia;
}TListaData;

typedef struct SListPreco
{
	float preco;
	TListaData data;
	// já não estás a usar estas variaveis
	//int ano, mes, dia;
	struct SListPreco *seg;
}TListaPreco;

typedef struct SListProduto
{
	char* nome;
	TListaPreco *preco;
	struct SListProduto *seg;
}TListaProduto;


TListaProduto* Adiciona(TListaProduto* lista, char* nome)
{
	TListaProduto* elemento = (TListaProduto*)malloc(sizeof(TListaProduto));
	if (elemento == NULL)
	{
		printf("Erro de alocacao\n");
		// É garantido a conversão implicita de zero para NULL,
		// mas é aconselhado usares NULL para ser mais explicito
		return 0;
	}

	elemento->nome = (char*)malloc(strlen(nome) + 1);

	strcpy(elemento->nome, nome);
	elemento->preco = NULL;
	// NOOP
	elemento->seg = NULL;
	elemento->seg = lista;
	lista = elemento;

	return lista;
}

TListaProduto *Insere(TListaProduto* lista, char* nome)
{
	if (lista == NULL || (strcmp(lista->nome, nome) > 0))
	{
		return Adiciona(lista, nome);
	}
	lista->seg = Insere(lista->seg, nome);

	return lista;
}
 
TListaPreco *AdicionaPreco(TListaPreco* lista, float preco, int ano, int mes, int dia)
{
	TListaPreco* elemento = (TListaPreco*)malloc(sizeof(TListaPreco));
	if (elemento == NULL)
	{
		printf("Erro de alocacao\n");
		return 0;
	}

	elemento->data.ano = ano;
	elemento->data.mes = mes;
	elemento->data.dia = dia;
	elemento->preco = preco;

	// aqui acho que querias dizer tail (último elemento da lista) em vez de head
	elemento->seg = NULL; // se o seguinte aponta para NULL é porque é a cabeça da lista
	elemento->seg = lista;
	lista = elemento; 

	return lista;
}

TListaPreco* InserePreco(TListaPreco* lista, float preco, int ano, int mes, int dia)
{
	if (lista == NULL)
	{
		return AdicionaPreco(lista, preco, ano, mes, dia);
	}
	lista->seg = InserePreco(lista->seg, preco, ano, mes, dia);

	return lista;
}

void InserePRECOS(TListaProduto* lista, char* nome, TListaPreco* precos)
{
	while (lista != NULL && (strcmp(lista->nome, nome) != 0))
	{
		lista = lista->seg;
	}
	if (lista != NULL)
	{
		lista->preco = precos;
	}
}

int ContaPrecos(TListaProduto* lista)
{
	int contagem = 0;
	// atribuição redundante com a linha seguinte
	TListaPreco* precos; //= lista->preco;

	precos = lista->preco;
	while (precos != NULL)
	{
		contagem++;
		precos = precos->seg;
	}

	return contagem;
}

void Imprime(TListaProduto* lista)
{
	if (lista == NULL)
	{
		// Caso queiras imprimir algo aqui, 
		// considero que seja mais correto dizer "Não existem Produtos para listar"
		// ou algo do género.
		// Caso não queiras imprimir nada, podes remover o if por completo, porque é lógica redundante com a condição do while
		//printf("--- fim da lista ---\n\n");
		return;
	}

	while (lista != NULL)
	{
		printf("- %s", lista->nome);
		printf(" #%d\n", ContaPrecos(lista));
		lista = lista->seg;
	}
}

void ImprimeLista(TListaProduto *lista)
{
	TListaProduto* atual = lista;
	TListaPreco* precoAtual;

	if (atual == NULL)
	{
		// Os mesmos comentários que Imprime
		//printf("--- fim da lista ---\n\n");
		return;
	}

	while (atual != NULL)
	{
		printf("- %s:\n", atual->nome);
		precoAtual = atual->preco;
		while (precoAtual != NULL)
		{
			// os valores de data estão em data
			//printf("- %d/%d/%d %.2f;\n", precoAtual->ano, precoAtual->mes, precoAtual->dia, precoAtual->preco);
			// adicionei padding and size specifiers para teres um output alinhado
			printf("\t- %04d/%02d/%02d %.2f\n", precoAtual->data.ano, precoAtual->data.mes, precoAtual->data.dia, precoAtual->preco);
			precoAtual = precoAtual->seg;
		}
		atual = atual->seg;
	}
}

// Não faz muito sentido retornares o ponteiro porque não há nada a fazer com o mesmo
// Se reparares a função free() também não tem qualquer retorno
//TListaPreco* LibertarPrecos(TListaPreco* lista)
void LibertarPrecos(TListaPreco* lista)
{
	// mudei o nome `precos` para `next` para se compreender melhor
	TListaPreco * next;
	while (lista != NULL)
	{
		next = lista->seg;
		free(lista);
		lista = next;
	}
}

// O mesmo que LibertarPrecos sobre retornar void
//TListaProduto *LibertarProduto(TListaProduto *lista)
void LibertarProduto(TListaProduto *lista)
{
	// mudei o nome `produto` para `next` para se compreender melhor
	TListaProduto * next;

	while (lista != NULL)
	{
		LibertarPrecos(lista->preco);
		free(lista->nome);
		next = lista->seg;
		free(lista);
		lista = next;
	}
}

int main()
{
	TListaProduto* listaProduto = NULL;
	TListaPreco* listaPreco = NULL;
	// mudei o nome `nome` para `linha` para melhor refletir o uso da mesma
	char linha[MAXSTR];
	char nomeAnterior[MAXSTR];
	// mudei o nome `procura` para `token` por ser um nome mais habitual
	char * token;
	char datapreco[MAXSTR][MAXSTR];
	char delimitador[] = " /";
	int i = 0;
    // Em vez de usares estas variaveis soltas, podes usar diretamente `TListaPreco`
	int dia = 0;
	int mes = 0;
	int ano = 0;
	float preco = 0;

	FILE* f;
	f = fopen("in.txt", "rt");
	if (f == NULL)
	{
		printf("Erro ao abrir ficheiro.\n");
		// convém terminares aqui, já que não podes fazer mais nada :P
		return EXIT_FAILURE;
	}

	/* 
	 * Iniciares um loop com feof não é correto,
	 * isto porque feof só indica EOF após uma tentativa de operação IO (neste caso um read (fgets)).
	 * Uma forma de testares isto é abrires um ficheiro vazio e testares feof antes e depois do read.
	 * Podes ler mais aqui: https://stackoverflow.com/questions/36164718/confusion-with-eof-vs-feof/36165010#36165010
	 */
	//while (!feof(f))
	while (fgets(linha, MAXSTR, f) != NULL)
	{
		if (linha[strlen(linha) - 1] == '\n')
		{
			linha[strlen(linha) - 1] = '\0';
		}

		if (isdigit(linha[0]))
		{
			token = strtok(linha, delimitador);
			while (token != NULL)
			{
				// caso o ficheiro esteja mal formatado e consigas tokenizar mais do que o esperado,
				// podes fazer overflow de `datapreco`
				strcpy(datapreco[i], token);
				i++;

				token = strtok(NULL, delimitador);
			}

			i = 0;
			ano = atoi(datapreco[0]);
			mes = atoi(datapreco[1]);
			dia = atoi(datapreco[2]);
			preco = atof(datapreco[3]);

			listaPreco = InserePreco(listaPreco, preco, ano, mes, dia);

			// Devias fazer este teste no inicio deste if,
			// já que não é permitido o ficheiro começar com linhas do tipo "data preço".
			// Estás também a fazer leak do item preço que acabaste de allocar caso isto seja falso.
			if (listaProduto != NULL)
			{
				InserePRECOS(listaProduto, nomeAnterior, listaPreco);
			}
		}
		//mas devido aos acentos tive que trocar a ordem dos if e retirar isalpha(nome[0])
		// ^ Consegues resolver isto com setlocale(LC_CTYPE, "") caso o teu locale default do sistema seja compativel
		// mas não recomendo trabalhares com chars fora do ASCII numa fase inicial
		else if (isalpha(linha[0]))
		{
			listaPreco = NULL;
			strcpy(nomeAnterior, linha);

			listaProduto = Insere(listaProduto, linha);
		}
	}

	// podes por exemplo testar aqui se o ciclo terminou por atingir EOF com feof()
	// ou se terá sido algum outro erro com ferror()

	if (listaProduto != NULL)
	{
		printf("Produtos:\n");
		ImprimeLista(listaProduto);

		puts("__________________");

		Imprime(listaProduto);

		LibertarProduto(listaProduto);
	}
	else
	{
		printf("erro ficheiro sem produtos fora\n");
	}

	return 0;
}

Em relação às funções para inserir elementos nas listas, tens uma outra alternativa mais comum que consiste em passares o endereço do ponteiro para o início da lista (duplo ponteiro). Desta forma consegues fazer um design mais flexível das funções, já que assim não necessitas de estar sempre a retornar o início da lista.  
Tens aqui uma outra sugestão de implementação com essas funções:

 

bool is_product_line(char * line)
{
	return isalpha(line[0]);
}

bool is_price_line(char * line)
{
	return isdigit(line[0]);
}

TListaPreco * parse_price_line(char * line)
{
	TListaPreco item;
	item.seg = NULL;

	int ret = sscanf(line, "%d/%d/%d %f", 
					 &item.data.ano, 
					 &item.data.mes, 
					 &item.data.dia,
					 &item.preco);
	if (ret != 4)
	{
		printf("Bad parsing of price line.\n");
		return NULL;
	}

    item.seg = NULL;

	TListaPreco * new_item;
	new_item = malloc(sizeof(*new_item));
	if (new_item == NULL)
	{
		printf("No memory.\n");
		return NULL;
	}

	*new_item = item;
	return new_item;
}

TListaProduto * parse_product_line(char * line)
{
	TListaProduto * new_item;
	new_item = malloc(sizeof(*new_item));
	if (new_item == NULL)
	{
		printf("No memory.\n");
		return NULL;
	}

    new_item->nome = malloc(strlen(line) + 1);
    if (new_item->nome == NULL)
    {
        printf("No memory.\n");
        return NULL;
    }

	strcpy(new_item->nome, line);
    new_item->preco = NULL;
    new_item->seg = NULL;

	return new_item;
}

void insert_product(TListaProduto ** list_product, TListaProduto * new_prod)
{
    TListaProduto ** cur;
    cur = list_product;

    while (*cur && strcmp((*cur)->nome, new_prod->nome) < 0)
    {
        cur = &(*cur)->seg;
    }

    new_prod->seg = *cur;
    *cur = new_prod;
}

void insert_price(TListaPreco ** list_price, TListaPreco * new_price)
{
    TListaPreco ** cur;
    cur = list_price;

    while (*cur) cur = &(*cur)->seg;

    new_price->seg = *cur;
    *cur = new_price;
}

int main()
{
	FILE * fp;
	fp = fopen("in.txt", "rt");
	if (fp == NULL)
	{
		printf("Erro ao abrir ficheiro.\n");
		return EXIT_FAILURE;
	}

	TListaProduto * list_product = NULL;
	TListaProduto * cur_product  = NULL;
	TListaPreco * list_price     = NULL;
	
	char line[MAXSTR];

	while (fgets(line, MAXSTR, fp) != NULL)
	{
		line[strcspn(line, "\n")] = '\0';

		if (is_product_line(line))
		{
			TListaProduto * new_product;
			new_product = parse_product_line(line);
			if (new_product == NULL) return EXIT_FAILURE;

            insert_product(&list_product, new_product);
			cur_product = new_product;
		}
		else if (is_price_line(line))
		{
			if (cur_product == NULL)
			{
				printf("File starts with price line.\n");
				return EXIT_FAILURE;
			}

			TListaPreco * new_price;
			new_price = parse_price_line(line);
			if (new_price == NULL) return EXIT_FAILURE;

            insert_price(&cur_product->preco, new_price);
		}
		else
		{
			// accept empty lines
			if (line[0] == '\0') continue;

			printf("Bad file format.\n");
			return EXIT_FAILURE;
		}
	}
	fclose(fp);

	printf("Produtos:\n");
	ImprimeLista(list_product);
	puts("__________________");
	Imprime(list_product);

	LibertarProduto(list_product);

	return 0;
}
Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.