yhara Posted January 7, 2021 at 08:27 PM Report Share #620916 Posted January 7, 2021 at 08:27 PM 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 comment Share on other sites More sharing options...
Solution yhara Posted January 10, 2021 at 11:59 AM Author Solution Report Share #620937 Posted January 10, 2021 at 11:59 AM 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 comment Share on other sites More sharing options...
rbsrbs_dev Posted January 17, 2021 at 01:31 AM Report Share #620992 Posted January 17, 2021 at 01:31 AM 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 comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now