Ir ao conteúdo

Posts recomendados

Postado

Boa tarde, pessoal. Estou com uma dificuldade em um exercício de lista encadeada. Trata-se de uma simulação de playlist por linha de comando. Consegui fazer um que o programa lê todos os dados do teclado, porém eu gostaria de já ter algumas músicas cadastradas em um arquivo CSV. O código a seguir é o que funciona.

 

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

int menu();
void InserirInicio(int num, char nome[20], char banda[20], int duracao[2]);
int Remover(int num);
void Listar();

struct playlist_Simples {
    int id; //criado número que será o identificador da música adicionada
	char nome[20]; //criado vetor para armazenar o nome da música com tamanho máximo de caracteres
    char banda[20]; //criado vetor para armazenar o nome da banda com tamanho máximo de caracteres
    int duracao[2]; //criado um vetor para armazenar os minutos e segundos em formato texto
	struct playlist_Simples *prox;
} *Head;

int  main() {
	int op, num, duracao[2];
    char nome[20], banda[20];

	Head = NULL;

	while (1) {
		op = menu();
		switch (op) {
		case 1:
			printf("Digite o ID da música: ");
			scanf("%d", &num);
            getchar();
            printf("Digite o nome da música: ");
            fgets(nome, 20, stdin);
            nome[strlen(nome) - 1] = '\0';
            printf("Digite o nome da banda: ");
            fgets(banda, 20, stdin);
            banda[strlen(banda) - 1] = '\0';
            printf("Digite o tempo de duração: ");
            scanf("%d:%d", &duracao[0], &duracao[1]);
			getchar();
			InserirInicio(num, nome, banda, duracao);
			break;
		case 2:
			int res;
			printf("Digite o numero a ser removido: ");
			scanf("%d", &num);
			getchar();
			res = Remover(num);
			if (res == 1)
				printf("Música removida.");
			else
				printf("Música não encontrada.");
			break;
		case 3:
			Listar();
			break;
		case 4:
			return 0;
		default:
			printf("Opção inválida.\n");
		}
	}
	return 0;
}

int menu() {
	int op;

	printf("1.Inserir música\n");
	printf("2.Remover música\n");
	printf("3.Listar músicas\n");
	printf("4.Sair\n");
	printf("Digite sua escolha: ");
	printf("\n\n");
	scanf("%d", &op);
	getchar();
	return op;
}

void InserirInicio(int num, char nome[20], char banda[20], int duracao[2]){
	struct playlist_Simples *NovoElemento;
	NovoElemento = (struct playlist_Simples *)malloc(sizeof(struct playlist_Simples));
	NovoElemento->id = num;
    strcpy(NovoElemento->nome, nome);
    strcpy(NovoElemento->banda, banda);
    NovoElemento->duracao[0] = duracao[0];
	NovoElemento->duracao[1] = duracao[1];
    

	if (Head == NULL){
		Head = NovoElemento;
		Head->prox = NULL;
	}
	else{
		NovoElemento->prox = Head;
		Head = NovoElemento;
	}
}


int Remover(int num){
	struct playlist_Simples *ElementoVarredura;
	ElementoVarredura = (struct playlist_Simples *)malloc(sizeof(struct playlist_Simples));
	 struct playlist_Simples *Anterior;
	Anterior = (struct playlist_Simples *)malloc(sizeof(struct playlist_Simples));

	ElementoVarredura = Head;
	while(ElementoVarredura != NULL){
		if(ElementoVarredura->id == num){
			if(ElementoVarredura == Head){
				Head = ElementoVarredura->prox;
				free(ElementoVarredura);
				return 1;
			}
			else{
				Anterior->prox = ElementoVarredura->prox ;
				free(ElementoVarredura);
				return 1;
			}
		}
		else{
			Anterior = ElementoVarredura;
			ElementoVarredura = ElementoVarredura->prox;
		}
	}
	return 0;
}

void Listar()
{
	struct playlist_Simples *ElementoVarredura;
	ElementoVarredura = (struct playlist_Simples *)malloc(sizeof(struct playlist_Simples));

	ElementoVarredura = Head;
	if (ElementoVarredura == NULL) {
		return;
	}
	while (ElementoVarredura != NULL) {
		printf("ID: %d; Nome: %s; Banda: %s; Duração: %d:%d\n", ElementoVarredura->id,
            ElementoVarredura->nome,
            ElementoVarredura->banda, ElementoVarredura->duracao[0], ElementoVarredura->duracao[1]);
		ElementoVarredura = ElementoVarredura->prox;
	}
	printf("\n\n");

	return;
}

Porém eu gostaria que esta lista fosse previamente preenchida com dados de um arquivo CSV.

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



struct playlist_Simples {
    char id; //criado número que será o identificador da música adicionada, em formato de caractere
	char nome[20]; //criado vetor para armazenar o nome da música com tamanho máximo de caracteres
    char banda[20]; //criado vetor para armazenar o nome da banda com tamanho máximo de caracteres
    char duracao[10]; //criado um vetor para armazenar os minutos e segundos em formato texto
	struct playlist_Simples *prox;
} *Head;

int main(){

    FILE *arquivo;
    arquivo = fopen("album.csv", "ra");

    while(feof(arquivo) == 0){
        char n[20];
        fgets(n, 20, arquivo);
        n[strlen(n) - 1] = '\0';
        printf("%s\n", n);
    }
    
    


    
    return 0;
}

 

Como eu faço pra jogar esses dados iniciais para a lista encadeada? Não estou achando uma função de leitura que eu consiga jogar na lista, para depois poder adicionar outras manualmente.

album.csv

  • Curtir 1
Postado

 

55 minutos atrás, kampa896 disse:

Como eu faço pra jogar esses dados iniciais para a lista encadeada? Não estou achando uma função de leitura que eu consiga jogar na lista, para depois poder adicionar outras manualmente

 

Escreva o simples

 

int  le_lista( const char* arq, Lista* plist);

 

retornando zero se ok, o comum em C, e chame

 

	Lista* plist = cria_lista();

	if ( le_lista("album.csv", plist) == 0) 
		printf("lista criada\n);

 

Não use esses nomes gigantes em coisas que vai usar toda hora:

 

    struct playlist_Simples* NovoElemento;
    NovoElemento = (struct playlist_Simples*)malloc(
        sizeof(struct playlist_Simples));

 

veja o tamanho disso. Dá muito trabalho pra ler, escrever e testar.

 

Escreva em torno dos dados e não misture as coisas

 

Seus dados são uma playlist, que é uma coleção de músicas. E vai por isso em uma lista encadeada, que nada tem a ver com música.

 

E mesmo assim seu programa tem UMA só estrutura, um nó da lista onde fica tudo misturado, A consequência é que vai ter um trabalho do inferno para ler e testar isso. E vai ser difícil usar em outros programas. E você sabe que depois desse vem aquele da biblioteca, o do estacionamento, o dos produtos e todos aqueles exercícios clássicos de lista.

 

struct playlist_Simples
{
    int id;  // criado número que será o identificador da
             // música adicionada
    char nome[20];  // criado vetor para armazenar o nome da
                    // música com tamanho máximo de
                    // caracteres
    char banda[20];  // criado vetor para armazenar o nome
                     // da banda com tamanho máximo de
                     // caracteres
    int duracao[2];  // criado um vetor para armazenar os
                     // minutos e segundos em formato texto
    struct playlist_Simples* prox;
} * Head;

 

 

Não poste anexos... É muito chato e inseguro pra quem quer te ajudar ter que sair do programa e fazer om download e salvar o arquivo e tudo mais. Ajude os outros a ajudarem você

 

 

Esse é o seu arquivo

 

1
Highway to Hell
AC/DC
3:28
2
Walk All Over You
AC/DC
5:09
3
Beating Around The Bush
AC/DC
3:56
4
Get It Hot
AD/DC
2:34
5
Love Hungry Man
AC/DC
4:17 

 

E não deve usar isso para um CSV. Nunca. Não use '\n' como delimitador. Deve imaginar que com esse nome o padrão deve ser a vírgula: Comma Separated Values = valores separados por vírgula, o formato de transporte de dados mais comum que existe e que só não tem um padrão na norma porque é anterior ao comitê ISO e a essas normas.

 

Use o simples

 

1,Highway to Hell,AC/DC,3:28
2,Walk All Over You,AC/DC,5:09
3,Beating Around The Bush,AC/DC,3:56
4,Get It Hot,AD/DC,2:34
5,Love Hungry Man,AC/DC,4:17 

 

E teste usando qualquer programa, como o Word, o Excel ou o Google Planilhas:

 

trecho.jpg.25620815d18042e90164840d5d4083f5.jpg

 

é muito mais simples

 

 

  • Obrigado 2
Postado

@arfnetosobre os anexos você tem razão, não irá ocorrer de novo.

 

E realmente, o exercício pediu uma simulação de playlist, por linha de comando, bem básica. Realmente não tem nada a ver, somente para fins didáticos.

Usei variáveis grandes para eu poder entender o que elas significam, mas entendi o ponto que atrapalha a leitura, implementação e teste.

 

Porém, cada nó dessa lista tem que ser uma música, e com o método que você utilizou aí em baixo eu ainda não consegui entender como meu programa vai ler os dados do arquivo CSV e inserir em cada nó da lista.

15 horas atrás, arfneto disse:
	Lista* plist = cria_lista();

	if ( le_lista("album.csv", plist) == 0) 
		printf("lista criada\n);

 

Eu só queria entender a lógica para utilizar funções de leitura de um arquivo e inserir em cada nó de uma lista. Apesar de você ter toda razão em eu não utilizar o '\n' como delimitador, foi o modo que consegui usando as funções de leitura. Mas mesmo assim eu apenas consegui ler e jogar na tela, e não em cada elemento da lista.

 

Mas suas observações são pertinentes como sempre, muito obrigado.

  • Obrigado 1
Postado
5 minutos atrás, kampa896 disse:

Eu só queria entender a lógica para utilizar funções de leitura de um arquivo

 

Não achou simples usar a função como eu te mostrei?

 

5 minutos atrás, kampa896 disse:

Apesar de você ter toda razão em eu não utilizar o '\n' como delimitador, foi o modo que consegui usando as funções de leitura. Mas mesmo assim eu apenas consegui ler e jogar na tela, e não em cada elemento da lista

 

pois é. Não conseguiu o que queria. Pode conseguir assim, mas não é esperto. Se fizer como eu te mostrei, e como se faz há mais de 40 anos, pode testar o arquivo com outros programas, como também te mostrei com o Planilhas.

 

Mesmo bancos de dados como o SQL Server e o SQL lite aceitam isso, e bancos de dados na nuvem como os da Amazon, Google e Microsoft. CSV é universal.

 

O comum: ler as linhas com fgets() e separar os campos com sscanf(). Eu já postei exemplos disso aqui. Se eu achar posto o link mais tarde. 

 

Entenda que um arquivo CSV é uma matriz MxN de campos separados obviamente pelo delimitador. M é o número de linhas e N o número de campos. Cada linha no texto tem EXATAMENTE N-1 delimitadores, 3 no seu caso.

 

Em geral a primeira linha tem o nome dos campos assim o importador pode nomear tudo certinho...

 

Veja como é esperto (minha opinião)

 

seq,musica,banda,tempo
1,Highway to Hell,AC/DC,3:28
2,Walk All Over You,AC/DC,5:09
3,Beating Around The Bush,AC/DC,3:56
4,Get It Hot,AD/DC,2:34
5,Love Hungry Man,AC/DC,4:17 

 

Vira

 

image.png.76f85426025cca4c7e269a67e2714fe8.png

Sem ter que fazer nada. No banco de dados já teria o nome das colunas da tabela. E sempre tem a opção de pular a primeira linha se não precisa dos nomes

 

Se não usa um campo apenas deixa os delimitares. No seu caso uma linha ", , ," seria válida.

16 minutos atrás, kampa896 disse:

eu ainda não consegui entender como meu programa vai ler os dados do arquivo CSV e inserir em cada nó da lista

 

Cada linha gera um nó.

  • Amei 1
Postado
13 minutos atrás, arfneto disse:

O comum: ler as linhas com fgets() e separar os campos com sscanf(). Eu já postei exemplos disso aqui. Se eu achar posto o link mais tarde. 

 

Muito obrigado, eu vou pesquisar sobre isso e aguardo caso você encontre esses exemplos.

Postado
47 minutos atrás, kampa896 disse:

Muito obrigado, eu vou pesquisar sobre isso e aguardo caso você encontre esses exemplos

 

meus registro --- 😞 tosco --- mostra que postei algo sobre isso por exemplo no início de julho de '20. Não achei o artigo ainda mas claro que pode pesquisar direto no meu conteudo no forum. Tem um botão lá.

 

Me lembro que postei também a definição e um documento que descreve csv e porque não tem uma norma ISO para isso. 

17 horas atrás, arfneto disse:

Escreva em torno dos dados e não misture as coisas

 

 

recomendo não minimizar o resto do que eu escrevi. Em especial a linha acima. Claro que pode dar certo do modo como está fazendo., mas não é bom. Não pode misturar as coisas. Entendeu o que eu disse sobre escrever em torno dos dados?

Postado

@arfnetoa disciplina é sobre estrutura de dados, porém não entendi exatamente. Entendi que você quis dizer que usar exemplos de playlist de música não teria sentido em usar lista.

 

Mas com seus ensinamentos localizei a função certa e como implementar no código. Muito obrigado, como sempre!

 

Aproveitando a oportunidade e fugindo do item do tópico, linguagem C consegue apagar dados de um arquivo? Nas aulas iniciais aprendi a manipular lendo e escrevendo, mas nunca pensei em apagar.

 

Neste exemplo, se eu quisesse apagar uma linha do arquivo CSV, teria como?

Postado
3 horas atrás, kampa896 disse:

a disciplina é sobre estrutura de dados, porém não entendi exatamente. Entendi que você quis dizer que usar exemplos de playlist de música não teria sentido em usar lista

 

Sempre venho com essa ladainha de "escrever em torno dos dados" e hoje estou com tempo para deixar um exemplo. Como já postei algo sobre CSV completo aqui mesmo não acho que tenha problema em eu postar código explicitamente sobre esse problema.

 

Afinal o que seria "escrever em torno dos dados"?

 

Essa é sua estrutura original, e as funções, no que podia ser um arquivo .h

 

struct playlist_Simples
{
    int id; 
    char nome[20]; 
    char banda[20];  
    int duracao[2];
    struct playlist_Simples* prox;
} * Head;

void InserirInicio(
    int num, char nome[20], char banda[20], int duracao[2]);
int  Remover(int num);
void Listar();

 

Eis o que acontece:

 

Head é o que? É struct playlist_Simples*, um ponteiro para uma estrutura.

InserirInicio é void() e tem nada menos que 4 argumentos. Isso é um desastre.

  • Se resolver colocar o nome do album e o ano vai mudar tudo? No programa inteiro? E se for escrever outro programa vai mudar tudo?
  • e se a lista virar um vetor? Vai perder tudo?
  • e se a lista não for de música?
  • e se só insere no início para que esse nome gigante?
  • não está claro que o que quer na verdade é inserir um item numa lista, seja lá o que for que tenha nela?
  • o mesmo se aplica a Listar: quer listar o conteúdo de uma lista
  • o mesmo se aplica a remover: quer remover o elemento de posição N numa lista. Cade a lista? E se precisar tratar 2? ou 30? Como vai ser

Escrevendo em torno dos dados

 

o que é o dado?

 

Musica é o dado.

 

typedef struct 
{
    int id;
    char nome[20]; 
    char banda[20];  
    int duracao[2];

} Musica;

 

O que é a estrutura de dados?


É uma lista encadeada. Lista é a estrutura de dados. 
 

Citação

A lista é de nós. Não é de músicas ou de livros ou de produtos ou de p. nenhuma. É de nós. Sempre de nós.


E cada nó aponta ou contém um dado. Muito mais conveniente.
 

Seja Info o dado...
 

typedef struct 
{
    int id;
    char nome[20]; 
    char banda[20];  
    int duracao[2];

} Musica;

typedef Musica Info;

typedef struct node_
{
    Info* info;
    struct node_* prev;
    struct node_* prox;

}   Node;

typedef struct
{
    Node* ini;
    Node* fim;
    size_t size;
    size_t lim;

}   Lista;

 

Entenda a diferença: a lista então é de nós. Cada nó aponta para um dado. Um dado nesse caso é uma musica mas se não for é só mudar o typedef.


E as funções?
 

int inserir(Info* info, Lista* lista);


veja inserir() como fazer mais sentido e tem mais futuro: recebe o endereço do elemento e o endereço de uma lista e faz o simples. Nada a ver com Musica. Vai poder usar isso sempre poruqe escreveu em torno dos dados. E das estruturas de dados. Isso é encapsulamento. E retorna zero se deu tudo certo. Isso é o comum em C.
 

int listar(Lista* lista, const char* titulo);


aqui listar() aceita um parâmetro, um título opcional porque é útil nos testes. Raramente se usa isso em produção afinal: em geral só se usa um elemento por vez e navega.
 

Lista* criar(size_t limite);


criar() cria uma lista com uma certa capacidade. Melhor ter um limite antes de seu programa (ou o de alguém que usa sua implementação de lista) entrar em loop e gerar milhares de registros antes de cancelar...

 

cansei de escrever mas acho que dá pra entender.

 

De volta ao CSV e ao programa
 

Arquivos em C
 

C foi escrita para escrever um sistema operacional, o Unix. E no Unix tudo é arquivo. A memória, os dispositivos, os processos e os arquivos. E C foi escrita por um grupo de gênios, gente como Kernighan, Ritchie, Bob Pike e Ken Thompson apenas... Então em C é trivial usar arquivos porque eles não iriam atirar nos próprios pés.
 

Veja essa função
 

int le_csv_v0(const char* arq)
{
    FILE* F = fopen(arq, "r");
    if (F == NULL) return -1;

    unsigned char buffer[256];
    size_t        lin = 0;
    char*         p   = fgets(buffer, sizeof(buffer), F);
    while (p != NULL)
    {
        size_t len = strlen(buffer) - 1;
        if (buffer[len] == '\n') buffer[len] = 0;
        printf(
            "%zd:\t[%s]\t(%zd)\n\n", ++lin, buffer,
            strlen(buffer));
        p = fgets(buffer, sizeof(buffer), F);
    }
    return 0;
};

 

Ele lê um arquivo texto linha a linha e mostra na tela. Nada mais. Parece complicado? 

 

Uma linha do seu arquivo

 

1,Highway to Hell,AC/DC,3:28

 

Se isso é uma linha de um arquivo CSV e lá estão 3 vírgulas então são 4 campos.
 

Citação


todo arquivo CSV pode ser carregado como char ** ** CSV;

 


Mas é complicado ir direto para o lado genérico. Se você quer resolver seu problema em minutos vai fazer o simples para 4 campos então. 

 

partindo da função cima...

 

Se pegar cada linha e separar os campos usando sscanf() acha que vai ter trabalho? Considere

 

unsigned char buffer[256];
char f[4][256];

 

Para segurança: um vetor de 4 campos do tamanho da linha de entrada. E cada linha foi lida em buffer na função acima.

 

int n = sscanf(buffer, mascara, f[0], f[1], f[2], f[3]);

 

O simples: tenta ler os quatro campos a partir de cada linha do arquivo. Claro que podia ler usando fscanf() direto do disco, mas assim é mais simples porque afinal pode escrever a partir da _v0() acima.
 

Lógico que se scanf() não retornar 4 vai cancelar o programa e deixar pra lá. Essa é a beleza dos programas de estudante afinal.

 

Se retornar 4 pode mostrar os campos pra testar:

 

printf( "Campos: [%s] [%s] [%s] [%s]\n\n", f[0], f[1], f[2], f[3]);

 

E o que é a tal mascara do sscanf()?
 

Os campos tem qualquer coisa menos a vírgula, que é o delimitador. E são 4. Algo assim não seria um campo: %[^,]? Seria: o circunflexo inverte e os colchetes indicam um grupo de letras então esse aí vai aceitar tudo até a vírgula mas sem ela.

 

Então
 

const char mascara[40] = "%[^,],%[^,],%[^,],%[^,\n]";


serve para esse caso.
 

E assim a versão 1 ficaria
 

int le_csv_v1(const char* arq)
{
    FILE* F = fopen(arq, "r");
    if (F == NULL) return -1;

    unsigned char buffer[256];
    size_t        lin         = 0;
    const char    mascara[40] = "%[^,],%[^,],%[^,],%[^,\n]";
    char          f[4][256];
    char*         p = fgets(buffer, sizeof(buffer), F);
    while (p != NULL)
    {
        size_t len = strlen(buffer) - 1;
        if (buffer[len] == '\n') buffer[len] = 0;
        printf(
            "%zd:\t[%s]\t(%zd)\n", ++lin, buffer,
            strlen(buffer));
        int n =
            sscanf(buffer, mascara, f[0], f[1], f[2], f[3]);
        if (n != 4)
        {
            fprintf(stderr, "Erro na entrada\n");
            fclose(F);
            return -1;
        }
        printf(
            "Campos: [%s]  [%s]  [%s]  [%s]\n\n", f[0], f[1],
            f[2], f[3]);
        p = fgets(buffer, sizeof(buffer), F);
    }
    fclose(F);
    return 0;
};


E se main fosse
 


int main(void)
{
    printf("versão 0\n\n");
    le_csv_v0("stuff.csv");

    printf("\n\nversao 1\n\n");
    le_csv_v1("stuff.csv");
}


e stuff.csv fosse
 

forum,clube,do,hardware
seq,musica,banda,tempo
1,Highway to Hell,AC/DC,3:28
2,Walk All Over You,AC/DC,5:09
3,Beating Around The Bush,AC/DC,3:56
4,Get It Hot,AD/DC,2:34
5,Love Hungry Man,AC/DC,4:17


iria mostrar
 

versão 0

1:      [forum,clube,do,hardware]       (23)

2:      [seq,musica,banda,tempo]        (22)

3:      [1,Highway to Hell,AC/DC,3:28]  (28)

4:      [2,Walk All Over You,AC/DC,5:09]        (30)

5:      [3,Beating Around The Bush,AC/DC,3:56]  (36)

6:      [4,Get It Hot,AD/DC,2:34]       (23)

7:      [5,Love Hungry Man,AC/DC,4:17]  (28)



versão 1

1:      [forum,clube,do,hardware]       (23)
Campos: [forum]  [clube]  [do]  [hardware]

2:      [seq,musica,banda,tempo]        (22)
Campos: [seq]  [musica]  [banda]  [tempo]

3:      [1,Highway to Hell,AC/DC,3:28]  (28)
Campos: [1]  [Highway to Hell]  [AC/DC]  [3:28]

4:      [2,Walk All Over You,AC/DC,5:09]        (30)
Campos: [2]  [Walk All Over You]  [AC/DC]  [5:09]

5:      [3,Beating Around The Bush,AC/DC,3:56]  (36)
Campos: [3]  [Beating Around The Bush]  [AC/DC]  [3:56]

6:      [4,Get It Hot,AD/DC,2:34]       (23)
Campos: [4]  [Get It Hot]  [AD/DC]  [2:34]

7:      [5,Love Hungry Man,AC/DC,4:17]  (28)
Campos: [5]  [Love Hungry Man]  [AC/DC]  [4:17]


Indo um passo por vez e escrevendo em torno dos dados...


E agora?
 

Se estivesse escrevendo assim já teria os campos todos. Para cada campo vai criar uma Musica e chamar insere para colocar na lista.

 

Nada mais.

 

Só que na hora de usar a lista não vai aparecer Musica porque está tudo separado. Encapsulamento: musica é um objeto. Lista é um container. E é chamado assim em C++. Ou coleção em java.

 

Só que para testar a lista não precisa de música. Para tratar a playlist não precisa de lista. Esse é o lance de OOP.

 

 

 

 

Escrevendo assim

  • você começa a testar mais rápido: levou minutos digitando os dados e o header primeiro
  • é mais fácil de reusar
  • rodou certinho da primeira vez em cada caso 😉 até prova em contrário

O programa de exemplo

 

😞 esqueci de postar

 

al.h

 

#pragma once
#include <stdio.h>

typedef struct
{
    int  id;
    char nome[20];
    char banda[20];
    int  duracao[2];

} Musica;

typedef Musica Info;

typedef struct node_
{
    Info*         info;
    struct node_* prev;
    struct node_* prox;

} Node;

typedef struct
{
    Node*  ini;
    Node*  fim;
    size_t size;
    size_t lim;

} Lista;

Lista* apagar(Lista* lista);
Lista* criar(size_t limite);
int    inserir(Info* info, Lista* lista);
int    remover(size_t n, Lista* lista);
int    listar(Lista* lista, const char* titulo);

 

al.c

 

#include "al.h"

Lista* apagar(Lista* lista) { return NULL; }

Lista* criar(size_t limite) { return NULL; }

int inserir(Info* info, Lista* lista) { return 0; }

int remover(size_t n, Lista* lista) { return 0; }

int listar(Lista* lista, const char* titulo) { return 0; }

 

main.c

#include <stdio.h>
#include <string.h>
#include "al.h"

int le_csv_v0(const char* arq);
int le_csv_v1(const char* arq);

int main(void)
{
    printf("versão 0\n\n");
    le_csv_v0("stuff.csv");

    printf("\n\nversao 1\n\n");
    le_csv_v1("stuff.csv");
}

int le_csv_v0(const char* arq)
{
    FILE* F = fopen(arq, "r");
    if (F == NULL) return -1;

    unsigned char buffer[256];
    size_t        lin = 0;
    char*         p   = fgets(buffer, sizeof(buffer), F);
    while (p != NULL)
    {
        size_t len = strlen(buffer) - 1;
        if (buffer[len] == '\n') buffer[len] = 0;
        printf(
            "%zd:\t[%s]\t(%zd)\n\n", ++lin, buffer,
            strlen(buffer));
        p = fgets(buffer, sizeof(buffer), F);
    }
    return 0;
};

int le_csv_v1(const char* arq)
{
    FILE* F = fopen(arq, "r");
    if (F == NULL) return -1;

    unsigned char buffer[256];
    size_t        lin         = 0;
    const char    mascara[40] = "%[^,],%[^,],%[^,],%[^,\n]";
    char          f[4][256];
    char*         p = fgets(buffer, sizeof(buffer), F);
    while (p != NULL)
    {
        size_t len = strlen(buffer) - 1;
        if (buffer[len] == '\n') buffer[len] = 0;
        printf(
            "%zd:\t[%s]\t(%zd)\n", ++lin, buffer,
            strlen(buffer));
        int n =
            sscanf(buffer, mascara, f[0], f[1], f[2], f[3]);
        if (n != 4)
        {
            fprintf(stderr, "Erro na entrada\n");
            fclose(F);
            return -1;
        }
        printf(
            "Campos: [%s]  [%s]  [%s]  [%s]\n\n", f[0], f[1],
            f[2], f[3]);
        p = fgets(buffer, sizeof(buffer), F);
    }
    fclose(F);
    return 0;
};

 

 

  • Curtir 1
  • Amei 1
Postado

@arfnetomeu amigo, só tenho a agradecer sua paciência em me dar essa aula em C. Tem muito material que você postou para eu ler com calma e pesquisar. Mais uma vez, obrigado!

Postado

Olá

 

8 horas atrás, kampa896 disse:

@arfnetomeu amigo, só tenho a agradecer sua paciência em me dar essa aula em C. Tem muito material que você postou para eu ler com calma e pesquisar. Mais uma vez, obrigado!

 

Acho que não tem o que "pesquisar". E isso que eu disse serve para qualquer linguagem: escreva em torno dos dados. E por consequência em torno das estruturas de dados.

 

Entenda que só peguei essa função

 

int le_csv_v0(const char* arq)
{
    FILE* F = fopen(arq, "r");
    if (F == NULL) return -1;

    unsigned char buffer[256];
    size_t        lin = 0;
    char*         p   = fgets(buffer, sizeof(buffer), F);
    while (p != NULL)
    {
        size_t len = strlen(buffer) - 1;
        if (buffer[len] == '\n') buffer[len] = 0;
        printf(
            "%zd:\t[%s]\t(%zd)\n\n", ++lin, buffer,
            strlen(buffer));
        p = fgets(buffer, sizeof(buffer), F);
    }
    return 0;
};

 

que lê as linhas de um arquivo e mostra na tela e usei em um programa de uma linha para chamar tal função. Levou 5 min.

 

int main(void)
{
    printf("versão 0\n\n");
    le_csv_v0("stuff.csv");

 

O arquivo copiei dos seus dados

 

Depois como já estava funcionando acrescentei mais umas linhas na _v1() para mostrar os campos. Mais 5 min. E coloquei mais 2 linhas em main()

 

int main(void)
{
    printf("versão 0\n\n");
    le_csv_v0("stuff.csv");

    printf("\n\nversao 1\n\n");
    le_csv_v1("stuff.csv");
}

 

Quanto aos dados usei sua definição de Musica e tirei os comentários. E acrescentei a definição de Node e Lista genérica. Mais 5 min. Não criei nada. Mesmo caso das funções, só deixei placeholders e estão em branco.

 

Só escrevi muito 😞 descrevendo o processo porque achei que seria legal ter um exemplo de como escrever de modo seguro sem perder tempo, usando os dados e não a linguagem.

 

Se der tempo depois coloco as funções de lista.

 

 

 

 

 

 

 

 

 

  • Amei 1
Postado
3 horas atrás, arfneto disse:

Só escrevi muito 😞 descrevendo o processo porque achei que seria legal ter um exemplo de como escrever de modo seguro sem perder tempo, usando os dados e não a linguagem.

Eu agradeço toda atenção e todo ensinamento compartilhado. Vou reler o material de estrutura de dados com uma outra visão depois dessa aula com esta visão simples, porém poderosa e objetiva!

Postado

Escrevendo em torno dos dados. E das estruturas de dados.


Seguindo um pouco adiante

 

No fundo vamos mudar apenas 2 linhas da versão 1 e incluir uma nova função e já vai aparecer a lista ligada com o conteúdo do arquivo, a playlist na ordem original das músicas no arquivo csv. Usando orientação a objetos e SRP.


Essa é de novo a estrutura original, e as funções, no que podia ser um arquivo .h

 

struct playlist_Simples
{
    int id; 
    char nome[20]; 
    char banda[20];  
    int duracao[2];
    struct playlist_Simples* prox;
} * Head;

void InserirInicio(
    int num, char nome[20], char banda[20], int duracao[2]);
int  Remover(int num);
void Listar();

 

E tem os problemas que eu listei antes

 

com le_csv_v0() e le_cvs_v1() dos posts anteriores e esse arquivo

 

1,Highway to Hell,AC/DC,3:28
2,Walk All Over You,AC/DC,5:09
3,Beating Around The Bush,AC/DC,3:56
4,Get It Hot,AD/DC,2:34
5,Love Hungry Man,AC/DC,4:17

 

temos esse resultado com as versões 0 e 1

 

versão 0

1:      [forum,clube,do,hardware]       (23)

2:      [seq,musica,banda,tempo]        (22)

3:      [1,Highway to Hell,AC/DC,3:28]  (28)

4:      [2,Walk All Over You,AC/DC,5:09]        (30)

5:      [3,Beating Around The Bush,AC/DC,3:56]  (36)

6:      [4,Get It Hot,AD/DC,2:34]       (23)

7:      [5,Love Hungry Man,AC/DC,4:17]  (28)



versão 1

1:      [forum,clube,do,hardware]       (23)
Campos: [forum]  [clube]  [do]  [hardware]

2:      [seq,musica,banda,tempo]        (22)
Campos: [seq]  [musica]  [banda]  [tempo]

3:      [1,Highway to Hell,AC/DC,3:28]  (28)
Campos: [1]  [Highway to Hell]  [AC/DC]  [3:28]

4:      [2,Walk All Over You,AC/DC,5:09]        (30)
Campos: [2]  [Walk All Over You]  [AC/DC]  [5:09]

5:      [3,Beating Around The Bush,AC/DC,3:56]  (36)
Campos: [3]  [Beating Around The Bush]  [AC/DC]  [3:56]

6:      [4,Get It Hot,AD/DC,2:34]       (23)
Campos: [4]  [Get It Hot]  [AD/DC]  [2:34]

7:      [5,Love Hungry Man,AC/DC,4:17]  (28)
Campos: [5]  [Love Hungry Man]  [AC/DC]  [4:17]

 

A terceira versão

 

A motivação:
 

A ideia é não perder tempo e terminar isso em menos de 1h, sem stress e sem voltar atrás. Escrevendo em torno dos dados e com um olho em como reaproveitar as funções da lista encadeada nos próximos programas

 

A próxima versão:

 

	Lista* le_csv_v2(const char* arq)

 

Assim se poderia escrever apenas 
 

    Lista* teste = le_csv_v2("stuff.csv");

 

E teste seria a lista com as músicas. Nada mal 😉 se funcionar...

 

Como os campos já estão divididos a partir do arquivo de entrada e a Lista agora é 

 

typedef Musica Info;

typedef struct node_
{
    Info*         info;
    struct node_* prev;
    struct node_* prox;

} Node;

typedef struct
{
    Node*  ini;
    Node*  fim;
    size_t size;
    size_t lim;

} Lista;

 

Seria bem conveniente que a _v2() simplesmente retornasse uma Lista contendo as músicas da entrada ou NULL se não achar ao menos uma música válida no arquivo de entrada. O clássico em C.

 

É claro que se pode ler as músicas e processar tudo linha a linha a partir do arquivo, mas

  • existe a ideia de terminar isso em menos de 1h, sem ficar mexendo de novo no que está funcionando
  • usando orientação a objetos (encapsulamento) e o princípio de responsabilidade única pode ser mais simples só retornar a Lista

depois de tudo funcionando se pode voltar atrás e eliminar passos intermediários se for importante

 

Onde _v1() parou?

 

        int n =
            sscanf(buffer, mascara, f[0], f[1], f[2], f[3]);
        if (n != 4)
        {
            fprintf(stderr, "Erro na entrada\n");
            fclose(F);
            return -1;
        }
        printf(
            "Campos: [%s]  [%s]  [%s]  [%s]\n\n", f[0],
            f[1], f[2], f[3]);

 

o printf() mostra os campos são separados em f por sscanf(). E f é o que? char[4][256].

 

    char f[4][256];

 

E como inserir na lista? Temos as funções --- ainda a escrever:

 

Lista*  apagar(Lista*);
Lista*  criar(size_t);
int     inserir(Info*, Lista*);
int     listar(Lista*, const char*);
int     remover(size_t, Lista*);


Claro que nesse caso Info é Musica.

 

    typedef Musica Info;


Se existisse uma função
 

    Musica* criar_musica(char[4][256]);


seria o caso de passar esses campos f[] para ela e depois inserir direto a Musica na lista, o esperado.

E na saída teria o resultado esperado: a playlist. Claro que seria legal inserir() colocar o novo nó sempre no final da lista, para não inverter a ordem da playlist do arquivo

 

A versão 2 então teria isso no lugar do printf() por exemplo

 

        Musica* mus = criar_musica(f);
        if (mus != NULL) inserir(mus, nv), free(mus);

 

O simples: se os campos lidos são uma música válida insere na lista. E libera a memória.

Citação

Note que tem se ser ',' e não ';' no if porque não tem chaves

 

main() para esse caso:

 

int main(void)
{
    printf("\n\nversao 2\n\n");
    Lista* teste = le_csv_v2("stuff.csv");
    listar(teste,"\nMusicas lidas do arquivo CSV");
    teste = apagar(teste);
    return 0;
}

 

E a saída

 

versão 2

1:      [1,Highway to Hell,AC/DC,3:28]  (28)
2:      [2,Walk All Over You,AC/DC,5:09]        (30)
3:      [3,Beating Around The Bush,AC/DC,3:56]  (36)
4:      [4,Get It Hot,AD/DC,2:34]       (23)
5:      [5,Love Hungry Man,AC/DC,4:17]  (28)

Musicas lidas do arquivo CSV

Lista com 5 de 100 elementos MAX

    Primeiro:    1
      Ultimo:    5

Elementos

       1  Highway to Hell AC/DC   3:28
       2  Walk All Over You AC/DC   5:09
       3  Beating Around The  AC/DC   3:56
       4  Get It Hot AD/DC   2:34
       5  Love Hungry Man AC/DC   4:17

Fim da lista

 

Como nada foi mudado no resto do código só é preciso testar as novas funções. Essa é o lance dos objetos e do SRP.

 

Como seria criar_musica()

 

Como o único lugar onde isso é tratado é na função --- SRP --- nem é preciso usar os dados do arquivo para testar a lista:

 

#include "info.h"

Musica* criar_musica_fake(char f[4][256])
{
    printf(
        "Campos: [%s]  [%s]  [%s]  [%s]\n\n", f[0], f[1],
        f[2], f[3]);

    static int id = 1;
    Musica*    m  = (Musica*)malloc(sizeof(Musica));
    if (m == NULL) return NULL;
    sprintf(m->nome, "Musica-%05d", id);
    sprintf(m->banda, "Banda-%05d", id);
    m->duracao[0] = id % 10;      // min 0..9
    m->duracao[1] = 1 + id % 58;  // seg 1..59
    m->id         = id++;
    return m;
};  // criar()

 

E na saída

 

versão 2

1:      [1,Highway to Hell,AC/DC,3:28]  (28)
Campos: [1]  [Highway to Hell]  [AC/DC]  [3:28]

2:      [2,Walk All Over You,AC/DC,5:09]        (30)
Campos: [2]  [Walk All Over You]  [AC/DC]  [5:09]

3:      [3,Beating Around The Bush,AC/DC,3:56]  (36)
Campos: [3]  [Beating Around The Bush]  [AC/DC]  [3:56]

4:      [4,Get It Hot,AD/DC,2:34]       (23)
Campos: [4]  [Get It Hot]  [AD/DC]  [2:34]

5:      [5,Love Hungry Man,AC/DC,4:17]  (28)
Campos: [5]  [Love Hungry Man]  [AC/DC]  [4:17]


Musicas lidas do arquivo CSV

Lista com 5 de 100 elementos MAX

    Primeiro:    1
      Ultimo:    5

Elementos

       1  Musica-00001 Banda-00001   1:02
       2  Musica-00002 Banda-00002   2:03
       3  Musica-00003 Banda-00003   3:04
       4  Musica-00004 Banda-00004   4:05
       5  Musica-00005 Banda-00005   5:06

Fim da lista

 

pra que isso?

 

Simples: para não perder tempo tratando e convertendo os dados do disco pode ser muito melhor seguir adiante com dados conhecidos e simulados e testar logo as funções da lista ligada, que é o que importa

 

Em muitos casos são outras pessoas ou empresas que implementam partes diferentes do sistema e não dá pra esperar os dados oficiais por exemplo

 

Depois que a lista estiver ok e a listagem e tudo se pode voltar a isso. 

 

Eis uma versão de criar_musica() que converte os dados

 

Essa tem um mínimo de testes. Mas o que importa é que isso pode ser muito complicado e só tem a ver com ESSE programa então deve ser deixado para depois.

 

Musica* criar_musica(char f[4][256])
{
    if (strlen(f[0]) == 0) return NULL;
    if (strlen(f[1]) == 0) return NULL;
    if (strlen(f[2]) == 0) return NULL;
    if (strlen(f[3]) == 0) return NULL;
    // id deve ter valor posisivo
    int     id = atoi(f[0]);
    if (id <= 0) return NULL;    
    Musica* m = (Musica*)malloc(sizeof(Musica));
    if (m == NULL) return NULL;
    m->id = id;
    // nome
    size_t len = sizeof(m->nome);
    if (strlen(f[1]) >= len)
    {  // copia o que cabe
        memcpy(m->nome, f[1], len - 1);
        m->nome[len - 1] = 0;
    }
    else
        strcpy(m->nome, f[1]);
    // banda
    len = sizeof(m->banda);
    if (strlen(f[2]) >= len)
    {  // copia o que cabe
        memcpy(m->banda, f[2], len - 1);
        m->banda[len - 1] = 0;
    }
    else
        strcpy(m->banda, f[2]);
    // duracao
    int min = 0;
    int seg = 0;
    int n   = sscanf(f[3], "%d:%d", &min, &seg);
    if (  // testes basicos com a duracao
        ( n != 2 ) || 
        min<0 ||
        (min == 0 && seg == 0) || 
        min > 590 ||
        seg < 0 ||
        seg>59 )
    {
        free(m);
        return NULL;
    }
    m->duracao[0] = min;
    m->duracao[1] = seg;
    return m;
};  // criar()

 

E a estrutura de dados afinal?

 

As funções são genéricas e todas muito simples

 

um protótipo de criar()

 

Lista* criar(size_t limite)
{
    Lista* nova = (Lista*)malloc(sizeof(Lista));
    if (nova == NULL) return NULL;
    nova->size = 0;
    nova->lim  = limite;
    nova->ini  = NULL;
    nova->fim  = NULL;
    return nova;
}

 

um protótipo de apagar()

 

Lista* apagar(Lista* l)
{
    if (l == NULL) return NULL;
    if (l->size == 0)  // vazia
    {
        free(l);
        return NULL;
    };  // if()
    // tem alguma coisa: apaga todo mundo
    Node* node = l->ini;
    do {
        Node* p = node->prox;  // salva esse ponteiro
        free(node);
        node = p;
    } while (node != NULL);
    return NULL;
};  // apagar()

 

Nada especial, apenas o loop apagando os nós e liberando a memória. Retornar NULL é importante porque permite escrever coisas como
 

    teste = apagar(teste);

 

garantindo que o ponteiro para a lista seja invalidado na MESMA LINHA que apaga a lista.

 

inserir()

 

int inserir(Info* item, Lista* l)
{  // insere no fim da lista
    if (l == NULL) return 0;
    if ((l->lim > 0) && (l->size >= l->lim)) return -1;
    // cabe: cria um novo no e poe la no fim
    Node* novo = (Node*)malloc(sizeof(Node));
    if (novo == NULL) return -1; // não alocado
    // se estava vazio entao inicia os ponteiros
    if (l->size == 0)
    {
        l->ini     = novo;
        l->fim     = novo;
        novo->prev = NULL;
        novo->prox = NULL;
    }
    else
    {
        novo->prev     = l->fim;  // o ultimo de antes
        (l->fim)->prox = novo;
        novo->prox     = NULL;  // depois não tem ninguem
        l->fim         = novo;
    };
    novo->info = (Info*)malloc(sizeof(Info));
    if (novo->info == NULL)
    { 
        free(novo);
        return -2;
    }
    *(novo->info) = *item;  // copia
    l->size += 1;
    return 0;
};

 

Nada especial. Insere no fim para não mexer na ordem dos dados.

 

Em geral se tem as quatro funções: inserir e remover no inicio e no fim porque são as disciplinas de fila normais: FIFO e LIFO. Em C++ seriam

  • push_back
  • push_front
  • pop_back
  • pop_front

da fila deque.

 

remover() o n-ésimo

 

int remover(size_t n, Lista* lista)
{  // isso não e algo comum em listas
    if (lista == NULL) return -1;
    if (lista->size < n) return -2;  // nem tem tantos
    Node* p = lista->ini;
    // posiciona no registro n
    for (size_t i = 0; i < n - 1; i += 1) p = p->prox;
    //listar_item(p->info, "remover()");
    // sera o unico?
    if (lista->size == 1)
    {   // vai ficar vazia
        lista->ini = NULL;
        lista->fim = NULL;
        lista->size = 0;
        free(p);
        return 0;
    }
    // apaga as referencias a p
    if (p->prev != NULL)
        p->prev->prox = p->prox;
    else
        lista->ini = p->prox;  // novo primeiro

    if (p->prox != NULL)
        p->prox->prev = p->prev;
    else
        lista->fim = p->prev;  // novo ultimo

    lista->size -= 1;
    free(p); // apaga mesmo
    return 0;
}

 

Isso é o simples. Claro que em geral só se usa 

 

    remover(1, teste);
    remover(teste->size, teste);

 

para remover o primeiro ou o último.

 

listar()

 

Aqui precisa afinal saber da musica, ao menos num exercício para iniciantes. Em OOP se usaria um ponteiro para uma função que lista a música --- ou seja lá o que for Info --- e no caso desse exemplo um getter para retornar as chaves do primeiro e do ultimo registro, para ficar genérico de verdade.

 

int listar(Lista* l, const char* titulo)
{
    if (l == NULL) return -1;
    if (titulo != NULL) printf("%s\n", titulo);
    if (l->lim > 0)
        printf(
            "\nLista com %zd de %zd elementos MAX\n",
            l->size, l->lim);
    else
        printf(
            "\nLista com %zd elementos [MAX não "
            "definido]\n",
            l->size);
    if (l->size < 1) return 0;

    Node* p = l->ini;
    printf("\n    Primeiro: %4d\n", l->ini->info->id);
    printf("      Ultimo: %4d\n", l->fim->info->id);

    printf("\nElementos\n\n");
    do {
        listar_item(p->info,NULL);
        p = p->prox;
    } while (p != NULL);
    printf("\nFim da lista\n\n");
    return 0;
};

 

Um programa completo de exemplo pode ser visto ou baixado aqui. 

 

Não testei quase nada e só rodei no Visual Studio porque me era mais conveniente.

 

Entenda que isso serve para qualquer lista ou outra estrutura

 

Uma outra função importante seria inserir na ordem e dependeria de ter essa noção (de ordem) nos dados e uma função de comparação. Pode até ser totalmente genérico se usar uma lista de ponteiros para funções que tratam os dados, como implicitamente acontece em C++ ou java

 

 

  • Curtir 1
  • Amei 1
  • 5 semanas depois...
Postado

@arfneto  desculpa a demora pra responder. Serviço e faculdade não tá fácil. Mas, mais uma vez, outra aula de linguagem C. Entreguei meu trabalho de uma forma mais simples, mas estou estudando estas formas que você me passou. Muito obrigado!

Postado
Em 16/05/2022 às 16:51, arfneto disse:

Entenda que isso serve para qualquer lista ou outra estrutura

 

 

Considere esses exemplos. Servem para qualquer estrutura. Apenas copiando e alterando o conteúdo. E a parte do CSV também pode consumir qualquer tipo de arquivo...

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

GRÁTIS: ebook Redes Wi-Fi – 2ª Edição

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!