Jump to content

Vinicius Antunes Osti

Membros
  • Content Count

    8
  • Joined

  • Last visited

  • Country

    Brazil

Community Reputation

2 Neutral

Recent Profile Visitors

68 profile views
  1. Obrigado pelo toque, Frederico! Meu parser está bastante ad hoc, pensei em resolver de imediato o problema do Felipe e não me ative em resolver o problema da leitura do CSV de uma maneira mais abrangente. Apenas para registro, o que vi que está errado no meu parser: Meu parser sempre vai procurar por duas aspas, ignorando tudo o que vem antes e depois delas; Não é uma boa ideia usar o strtok com "," como separador se um campo possuir uma vírgula. Meu parser não está lidando com caracteres especiais no contexto do CSV, como quebras de linha e as próprias aspas duplas. Usar fgets para ler um registro confiando que nenhum campo conterá quebras de linha pode ser problemático. Se eu errei também na minha retratação, pode me descer o cascuco, Frederico. Mas no exemplo que você deu são 2 strings e 2 números, não? "fred", " o bom"", 10 e 3.14
  2. Ahhh, agora eu entendi o motivo do continue no código original! A primeira linha do CSV possui o descritor do campo que será preenchido. Nesse caso, basta adicionar uma linha de fgets antes do while para descartar a primeira linha. Algumas coisas ainda não estavam funcionando pois: - você não observou atentamente qual flag de formato você usa para um int: é %d ou %s? - você não modificou o loop conforme meu post anterior. Dá uma olhada no código agora. Provavelmente você verá alguns zeros depois que o merge sort for feito por inconsistência entre a quantidade de entradas no arquivo e na definição do tamanho do vetor de dataset, mas esse é um problema bem simples de resolver. main.c
  3. Se o seu CSV terá todos os campos sem aspas, basta modificar algumas linhas no loop while em main: antes: remove_quotes(token_wq, token); process_field(field_count++, token_wq, i); depois: process_field(field_count++, token, i); Se vocÊ quer lidar com dados que podem ou não ter aspas, eu fiz um comentário no post original: Ah, claro, erro meu. Eu fiz uma alteração que não expliquei. Alterei a função main para que ela receba argc e argv, para que eu pudesse testar com diferentes arquivos especificados na linha de comando. Para voltar à funcionalidade original, basta alterar a linha que lê o arquivo. Em vez de ler o arquivo argv[1], leia o arquivo "DT.csv". Pode-se também retirar o argc e argv dos argumentos de main como era antes.
  4. Eu não verifiquei a lógica do merge sort. As modificações que eu fiz foram apenas na parte de leitura dos arquivos, e minha "investigação" do seu código passou por 4 etapas: 1. A formatação do arquivo CSV Como eu não sabia se seu arquivo CSV estava com os dados dispostos em linhas ou colunas, criei um arquivo CSV com os dados dispostos em cada formato para ver o que acontecia com seu código: - Se os dados estiverem dispostos em coluna, a primeira entrada será ignorada, e o programa processará o CSV a partir da segunda linha. Aí, o programa dá erro; - Se os dados estiverem dispostos em linha, a primeira linha será ignorada, e o programa processará o CSV a partir da segunda linha (como no caso acima). Se os dados estiverem todos na mesma linha, essa linha será ignorada, o loop de leitura dos dados se encerrará, e com ele, o programa. O que estava provocando isso tudo era esse trecho bem estranho: while (fgets(buf, 1024, fp)) { row_count++; if (row_count == 1) { continue; } ... } Na leitura da primeira linha, como row_count sempre ficara igual a 1, o código entra nesse if e executa o comando continue, que encerra a iteração atual do loop e passa diretamente para a próxima. Se o CSV tiver apenas uma linha, o comando dentro do while já terá chegado no final do arquivo, o que encerra o loop, fechando o arquivo em seguida, e fechando o programa, sem fazer nada. Mas esse não era o único problema do código, tampouco o mais crítico. 2. A leitura dos dados do arquivo para o programa A lógica de leitura do arquivo parecia bastante complicada. Aparentemente, a intenção do código era encontrar onde estavam as aspas do CSV para passar o conteúdo entre aspas para uma outra função que deveria passar os dados para uma estrutura chamada "dataset". Ocorre que C possui umas ferramentas bem legais para manipulação de strings. Deixei o loop de leitura do arquivo assim: while (fgets(buf, 1024, fp)) { field_count = 0; token = strtok(buf, ","); while (token != NULL) { remove_quotes(token_wq, token); process_field(field_count++, token_wq, i); token = strtok(NULL, sep); } i++; printf("\n"); if (i > TAMANHO) break; } Primeiro, strtok (em string.h) procurará por ocorrências de "," em buf. Por exemplo, se a linha do csv for "bla","ble","bli", a primeira ocorrência de strtok gerará a string "bla". As ocorrências seguintes, onde NULL aparece como argumento, irão, em sequência, gerar "ble" e "bli". Como não há "," depois do "bli", strtok retornará NULL no token, indicando que aquela linha não possui mais tokens. Outra função que aparece é a remove_quotes. Essa função eu criei para eliminar as aspas das strings: void remove_quotes(char* dst, char* src) { char* quote; int first_quote; int last_quote; int len; quote = strchr(src, '"'); first_quote = quote - src; while (quote != NULL) { quote = strchr(quote+1, '"'); if (quote) { last_quote = quote - src; } } len = last_quote - first_quote - 1; strncpy(dst, src+first_quote+1, len); dst[len] = '\0'; } Essa função utiliza a função strchr, que localiza a primeira ocorrência de um char (que nesse caso, é um char de aspas duplas) dentro da string iniciada pelo ponteiro indicado (que aqui eu chamei de src). Essa função encontrará as primeiras aspas da string, armazena sua posição relativa na string de origem, e vai encontrando, em seguida, aspas duplas até a função strchr retornar NULL. A posição relativa das últimas aspas duplas é armazenada. Depois de encontrar a posição relativa das primeiras e últimas aspas duplas na string de origem (src), o tamanho da string entre aspas é calculada (len). No final, strncpy copia exatamente o tamanho necessário (len) a partir do primeiro caractere depois das primeiras aspas duplas da origem na string de destino (dst) Como a função strncpy não termina a string de destino com um caractere nulo, ele é adicionado. (dst[len] = '\0). Caso precise, é bem fácil modificar essa função caso a origem (src) já não possua aspas duplas. 3. Passando os dados para a estrutura dataset Essa parte lida com a função process_field. Mais coisas foram retiradas de lá do que colocadas, mas as alterações feitas nela são importantes para entender como funcionam os tipos de dados em C. A função process_field ficou bem enxuta: void process_field(int field_count, char *value, int i) { if (field_count == 0) { vet[i].ano = atoi(value); printf("Ano:\t%d", vet[i].ano); } } A função que interpreta o valor da string em value é a atoi. Ela "transforma" uma string em um número inteiro. Essa função se faz necessária pois uma string é um vetor de chars, e não são podem ser imediatamente convertidas para o número que é lido ou informado pelo usuário humano. Sem levar isso em consideração, algumas linhas do código original estavam tentando fazer essa conversão direta, como por exemplo: vet[i].ano = value; vet.ano é um número inteiro, e value é um ponteiro de char. Quando se realiza esse comando, o computador entenderá que você quer armazenar o valor de endereço de memória armazenado em value no campo ano de vet. Ainda que value aponte para uma string do tipo "1234", o valor de value, em si, nada tem a ver com o número que está sendo representado naquela string. Outro caso onde houve essa confusão foi nessa linha: printf("vetor[i] %s", vet[i].ano); Nesse comando, você especificouum formato %s para algo do tipo int, quando se espera que o tipo seja char*. Essa inconsistência pode gerar vários tipos de comportamentos indefinidos, como tentar ler vet.ano como um endereço de memória de uma sequência de chars terminadas em \0, ou outras coisas que podem travar o programa ou sua máquina, a depender do quão bem seu sistema lida com esse tipo de exceção. Outras linhas que eu removi por razões práticas, e que não fazem sentido foram essas: int tam = sizeof(vet[i].ano)/sizeof(int); merge(vet[i].ano,0,tam-1); tam sempre será igual a 1, pois vet.ano é inteiro, e sizeof(int)/sizeof(int) = 1. A linha que chama a função merge também não faz sentido, pois assim vcê estará fazendo vários merge sort de um único inteiro. Caso a intenção fosse inserir o ano na estrutura de modo a já fazer o sort e ter um vetor já arrumado logo após a inserção, até poderia ser feito durante a leitura dos dados, porém, o merge sort não funcionará eficientemente. Caso essa seja uma das necessidades da sua solução, procure por heap sort. 4. Corrigindo a tipagem dos parâmetros das funções do merge sort Por fim, alterei os tipos dos parâmetros das funções de sort. Como você está lidando com um vetor de dataset, onde cada dataset contém um int, as funções devem recever ponteiros para dataset, e não ponteiros para inteiro. //void intercala(int *x, int inicio, int fim, int meio) { void intercala(dataset* x, int inicio, int fim, int meio) { ... } //void merge(int *x, int inicio, int fim) { void merge(dataset* x, int inicio, int fim) { ... } As linhas que se referiam aos valores de x, agora fazem para x.ano. No fim, após a leitura dos dados, chamei a função merge e imprimi na tela o resultado: #define TAMANHO 103 ... int main(int argc, char** argv) { ... // executa o sort merge(vet, 0, TAMANHO-1); // print na tela for (j = 0; j < TAMANHO; ++j) { printf("%d\n", vet[j].ano); } return 0; } O código com todas as modificações estão em anexo main.c
  5. Vou tentar responder sua perguntas numa ordem que, creio, seja mais intuitiva. 0. "Eu sou burro" Não, vc não é. 1. "O que é std?" "std" é o namespace onde estão todas as coisas da biblioteca padrão de C++. Sempre que você incluir cabeçalhos da biblioteca padrão (exemplo: iostream, string, vector, etc), as funções, classes, templates, enums, etc, estarão dentro de std. std é abreviação de standard, que significa (ou se traduz como) padrão. 1.1 "O que é namespace?" É uma região que abstrai um "espaço de nome". Existe para diferenciar coisas de mesmo nome em códigos/bibliotecas diferentes. O Frederico deu um excelente exemplo na resposta acimaacima. 2. "O que é ::?" É o operador de escopo. - No caso de std::string, significa que string é um tipo/classe definido dentro do namespace std; - No caso de Aviao::imprimir(...), a função imprimir está dentro da classe Aviao. O uso de "Aviao::" é necessário pois se utiliza fora do escopo da declaração da classe Aviao 3. "O que é um construtor?" É um método que inicializa uma instância de uma determinada classe. Em C++, esse método tem o mesmo nome da classe.
  6. Dei uma olhada bem por cima no código. Algumas dicas, baseando-se que você está tentando imprimir a cartela com a sobrecarga do operador <<: 1. Quando copiar/colar ou adaptar um código encontrado na internet, tente entender o que o código faz. Sobrecarregar o operador << me parece ser uma tentativa de matar uma formiga com um revólver. Não tente dar um passo maior que a perna. 2. Uma solução simples e fácil de implementar envolveria um uso cuidadoso de um único laço for, do operador de resto de divisão inteira (%) e de funções do cabeçalho iomanip. Depois de entender como você quer a sua saída e como pode implementá-la, aí sim, tente fazer generalizações baseadas em recursos mais avançados de C++.
  7. Dei uma olhada no código e fiz algumas alterações, algumas simples, outras mais drásticas, mas mantendo o espírito do código original. Vou mostrando o código e explicando na sequência. //BIBLIOTECAS #include <stdio.h> #include <stdlib.h> #include <locale.h> #include <string.h> //CONSTANTES #define FILA_TAMANHO 20 #define ESTADO_TAMANHO 51+1 #define CIDADE_TAMANHO 51+1 #define BAIRRO_TAMANHO 51+1 #define RUA_TAMANHO 101+1 Mantive as mesmas bibliotecas. Adicionei mais constantes, baseadas nos tamanhos das strings da struct Denuncia. As definições das constantes estão em caixa alta por padrões adotados em outros códigos, porém isso é totalmente opcional. O +1 explicarei depois. //DEFINIÇÃO DAS STRUCUTS typedef struct Denuncia { char estado[ESTADO_TAMANHO]; char cidade[CIDADE_TAMANHO]; char bairro[BAIRRO_TAMANHO]; char rua[RUA_TAMANHO]; int numero; } Denuncia; typedef struct FilaDenuncias { Denuncia denuncias[FILA_TAMANHO]; int tamanhoDaFila; } FilaDenuncias; A struct Denuncia teve alterações nos campos numero, ini e fim. O campo numero não é mais um array de ints, em vez disso, passa a ser um int comum. Os campos ini e fim foram removidos pois nada no código fazia referência a eles. Além disso, criei um novo struct FilaDenuncias, que agrega tanto o array de denúncias quanto o total de denúncias inseridas. Note que não há mais declarações globais de variáveis. //PROTOTIPAÇÃO void Denuncia_Inserir(FilaDenuncias *fila); void Denuncia_Excluir(FilaDenuncias *fila); void Denuncia_Imprimir(FilaDenuncias *fila); int Menu_Executar(); void Pausa(); void Remover_EOL(char* str); Aqui começam as mudanças mais brabas. Como não há mais a fila global de denuncias, para cada operação que deve ser executada na fila, esta deve ser passada por referência, exceto na função Denuncia_Imprimir, onde é admissível passar a fila por valor. A função de exibir o menu passa também a gerenciar a entrada do usuário, por isso a mudança do nome para Menu_Executar. O retorno agora é int, pois a função retornará a opção do usuário. Outras duas funções foram adicionadas. Pausa foi criada para eliminar as chamadas do system("pause"), de modo a tornar o código mais portável. Remover_Char foi criada para eliminar caracteres específicos de uma string. Seu uso será justificado depois. Agora, para a função main: int main(){ // DECLARAÇÕES int op = -1; FilaDenuncias filaDenuncias; filaDenuncias.tamanhoDaFila = 0; // INCLUINDO PT-BR setlocale(LC_ALL, "Portuguese"); ... return 0; } Note que as estruturas que antes eram de escopo global passam a ser localmente declaradas em main. Uma fila de denúncias é inicializada. int main(){ ... while ((op = Menu_Executar()) != 0) { switch (op){ case 1: Denuncia_Inserir(&filaDenuncias); break; case 2: Denuncia_Imprimir(&filaDenuncias); break; case 3: Denuncia_Excluir(&filaDenuncias); break; default: printf("Opcao Inválida \n\n"); break; } Pausa(); } return 0; } No loop principal da aplicação, a condicional chama a própria função Menu_Executar. As funções agora necessitam da referência da fila de denúncias. Após cada ação, o programa pausa. Agora, a implementação de cada função //ADICIONAR ELEMENTO AO FIM DA FILA void Denuncia_Inserir(FilaDenuncias *fila){ int i = fila->tamanhoDaFila; if (fila->tamanhoDaFila == FILA_TAMANHO){ ... } else { ... } } Essa foi a função que teve as mudanças mais radicais. Na primeira linha, criei uma variável temporária "i" para não ter que escrever fila->tamanhoDaFila o tempo todo e deixar o código mais limpo. A verificação feita para não exceder o tamanho máximo do array de denúncias agora é feito com a constante definida no início do código. //ADICIONAR ELEMENTO AO FIM DA FILA void Denuncia_Inserir(FilaDenuncias *fila){ int i = fila->tamanhoDaFila; if (fila->tamanhoDaFila == FILA_TAMANHO){ ... } else { while(getchar() != '\n'); printf("Digite o Estado: "); fgets(fila->denuncias[i].estado, ESTADO_TAMANHO, stdin); Remover_EOL(fila->denuncias[i].estado); ... printf("Digite o Número do Imóvel: "); scanf("%d", &(fila->denuncias[i].numero)); fila->tamanhoDaFila++; printf("Denúncia cadastrada!\n"); } } A lógica principal da inserção de denúncias. A primeira linha (while(getchar() != '\n') limpa o buffer de teclas pressionadas (stdin) até encontrar um '\n'. Isso faz com que a primeira entrada da denúncia (estado) não seja ignorada com o '\n' gerado pelo pressionar do enter utilizado para entrar na opção. Os campos que são strings (char*) são preenchidos com a função fgets, e não mais com a função scanf. A função fgets lê bytes de um arquivo até um número especificado de bytes ou encontrar uma quebra de linha ou fim do arquivo (EOF). O arquivo que será lido na verdade é o stdin, que é um arquivo "interno" da linguagem que armazena as teclas pressionadas. Ao fim, os caracteres são escritos na string designada. Depois disso, é removido o caractere '\n' gerado por causa da entrada no fgets através da função Remover_EOL. O número de caracteres máximo definido no início é +1 para que se possa comportar esse '\n' adicional. Para a cidade, bairro e rua, a lógica é a mesma. O número é lido apenas uma vez. No fim, o tamanho da fila é alterado. //EXCLUIR O PRIMEIRO ELEMENTO DA FILA void Denuncia_Excluir(FilaDenuncias *fila){ if (fila->tamanhoDaFila == 0){ printf("Não há Denúncias cadastradas...\n"); printf("Cadastre as suas denúncias...\n\n"); }else { int i; for (i = 0; i < fila->tamanhoDaFila; i++){ fila->denuncias[i] = fila->denuncias[i+1]; } fila->tamanhoDaFila--; printf("Denúncia excluída!\n"); } } Esta é a função que exclui a primeira denúncia da fila. Não foi muito alterada, exceto pela remoção de uma linha que, creio eu, sobrescrevia a denúncia na antiga posição da denúncia mais nova, que é desnecessária, em termos práticos. //IMPRIMIR O CONTEÚDO DAS DENÚNCIAS void Denuncia_Imprimir(FilaDenuncias *fila){ int i; if (fila->tamanhoDaFila == 0){ printf("Não há denúncias cadastradas.\n"); printf("Cadastre alguma denúncia...\n\n"); }else { printf("DENÚNCIAS CADASTRADAS\n"); for (i = 0; i < fila->tamanhoDaFila; i++){ printf("\n"); printf("Estado: %s \n", fila->denuncias[i].estado); printf("Cidade: %s \n", fila->denuncias[i].cidade); printf("Bairro: %s \n", fila->denuncias[i].bairro); printf("Rua: %s \n", fila->denuncias[i].rua); printf("Número: %d \n\n", fila->denuncias[i].numero); } } } Função para imprimir na tela as denúncias. Pouca coisa mudou, também. No entanto, note o uso dos operadores . e ->. Deve-se tomar cuidado aqui e em outros pontos do código onde esses operadores são usados. A variável "fila" é um ponteiro para uma FilaDenuncias, e não uma FilaDenuncias em si! Para acessar os membros da estrutura FilaDenuncias a qual "fila" aponta, usa-se ->: fila->denuncias. Note, agora, que denuncias não é um ponteiro, ele é um próprio exemplo direto da struct denuncia. Portanto, para acessar os membros de denuncias basta usar o .: denuncias.estado. Juntando tudo, fila->denuncias.estado é o campo "estado" da estrutura "denuncias", que por sua vez é um campo de uma FilaDenuncias apontada por "fila". int Menu_Executar(){ int op; printf("Escolha uma opção:\n"); printf("1- Cadastrar Denúncia\n"); printf("2- Exibir Denúncia\n"); printf("3- Excluir Denúncia\n"); printf("0- Sair\n\n"); printf("Digite aqui: "); scanf("%d", &op); return op; } A função que antes apenas exibia o menu agora também gerencia a entrada da opção. void Pausa() { printf("Pressione enter para continuar...\n"); while(getchar() != '\n'); getchar(); } Essa função foi criada para lidar com a pausa e substituir as linhas system("pause"). A função system passa um comando para o sistema operacional executar, que nesse caso é "pause". Ocorre que a chamada "pause" funcionará apenas em Windows. De modo a tornar o programa agnóstico de SO, criei essa função. a linha while(getchar() != '\n') funciona como ele é usado na função de inserir denúncia, para que qualquer '\n' no fluxo de entrada não seja capturado pelo getchar() subsequente, fazendo que a pausa seja ignorada. void Remover_EOL(char* str) { char* posicao = strchr(str, '\n'); if (posicao != NULL) *posicao = '\0'; } Finalmente, a função que remove o '\n' no final da string. A função strchr retorna um ponteiro para a primeira ocorrência do char solicitado (que aqui é um '\n'. Se for encontrado, a linha *posicao = '\0'" faz com que o local onde aponta "posição" seja alterado para '\0', que indica final de uma string. O código completo tá em anexo. Boa sorte com os estudos e aproveite! dengue.c
  8. Essa função cria um nó com o dado "valor" e o acrescenta ao final da lista "l"
×
×
  • Create New...