Ir ao conteúdo

C Struct de arquivo csv com erro de reconhecimento de variáveis.


Ir à solução Resolvido por arfneto,

Posts recomendados

Postado

Pessoal, tô aqui pra pedir ajuda com uma struct que não dá certo de jeito nenhum...

O caso é o seguinte, tenho um arquivo de texto e o meu programa deve coletar os dados desse arquivo de texto usando uma struct e depois contar o número de linhas (pulando a primeira) e retornar esse valor, mas toda vez ele aponta q os dados a serem lidos não fazem parte de uma struct.

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


int main(){

struct dado 
    {
        int data;
        float temp;
        char time[64];
    };

    int i=0;
    char info[64];
    struct dado *dado_t = NULL;
    setlocale(LC_ALL,"portuguese");

    FILE *fp = fopen("camera_temp.csv","r");

        if (!fp){
            perror("Arquivo não encontrado");
            exit(-1);
        }

    fgets(dado_t,64,fp);

    while (fscanf(fp,"%d,%f,%s",&dado_t.data,&dado_t.temp,&dado_t.time) ==3) //3 é a qtd de colunas de dados, marca o fim da contagem.
        { i++; }
        
    printf("\n\nQuantidade de dados é %d\n\n",i);



    fclose(fp);

return 0;
}

camera_temp.csvBuscando informações...

  • Solução
Postado

Olá

 

Ainda está um pouco longe de funcionar.
 

image.png.3a9ae3499955dab07bdfe7d7e9d2fadd.png
 

Podia fazer um programa primeiro só para ler os dados do arquivo, para se assegurar de que está no caminho certo...

 

Podia usar nomes mais significativos que dado e dado_t por exemplo.

 

Se dado_t é um ponteiro para dado onde vai afinal gravar esses dados de que sdegundo o enunciado do tópico você nem precisa? Não declarou nada...
 

  Em 01/04/2020 às 19:00, Jony Leal disse:

coletar os dados desse arquivo de texto usando uma struct e depois contar o número de linhas (pulando a primeira) e retornar esse valor

Expandir  

 

Para que a struct se só quer contar os registros? E se na  primeira linha estão os nomes dos campos porque não ler afinal? E se o valor  é temperatura (é?) porque não chamar de temperatura, ou value já que é o nome original?  Ninguém leva a sério uma variável chamada temp ou aux afinal :) 

 

fscanf() retorna um número que você deveria tratar. É melhor assim. Um campo a menos e sua leitura pode ficar toda destruída... Recomendo muito SEMPRE fazer isso.

 

E se dado_t é um ponteiro e você passa o endereço dele para fscanf() tudo que vai conseguir é corromper o próprio valor do ponteiro. Que por sinal não aponta para nada já que você não alocou memória para nenhuma dessas struct....

 

Podia ter postado ao menos as primeiras linhas do csv direto para poupar trabalho. Afinal apenas as duas primeiras já serviriam. Esse é o sentido de um arquivo csv. A gente precisa da primeira linha para saber o número dos campos e o nome. E depois vem os campos, um registro por linha, um campo entre cada delimitador.

Veja a diferença:
 

image.png.7f3ccbf6a112f9c6e594934eabc18246.png

 

Compare com clicar em um anexo, baixar um programa, abrir um anexo em outro programa e depois voltar ao seu programa para tentar ajudar você. . .

 

De volta ao programa:

 

O CSV começa então pelo header, a linha que tem o nome dos campos. O formato dela não é novidade desde os anos 80

  • os nomes separados pelo delimitador que em geral é a vírgula. 
  • fscanf() então deve ler esses 3 campos sem problemas porque afinal foi esrita para isso nos '70: ler entrada formatada.

Qual o o formato?
 

  • um nome, uma vírgula, um nome, uma vírgula e o último nome.
  • Qual a máscara para ler qualquer coisa que não seja uma vírgula?
  • abcd] por exemplo quer dizer que aceita essas letras, quantas vier.
  • [^abcd] que dizer que aceita qualquer coisa exceto a b c e d.

 

Então [^,] lê o que tiver até a próxima vírgula. Como são 3 campos é só repetir. E desprezar as vírgulas.

 

Sua máscara para os dados está certinha.

 

 Um exemplo
 

O programa abaixo lê o arquivo e mostra os valores na tela assim

fscanf(3) leu 'Series' + 'Value' + 'Time'

  1:        0     37.9    2018-11-18T08:41:03-02:00
  2:        1     37.9    2018-11-18T08:41:13-02:00
  3:        2     37.9    2018-11-18T08:41:23-02:00
  4:        3     37.9    2018-11-18T08:41:33-02:00
...

 97:       96     35.8    2018-11-18T09:00:30-02:00
 98:       97     35.8    2018-11-18T09:00:40-02:00
 99:       98     35.8    2018-11-18T09:00:50-02:00

99 leituras no arquivo

O programa

#define _CRT_SECURE_NO_WARNINGS

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

struct dado
{
    int     c_series;
    float   c_value;
    char    c_time[64];
};
typedef struct dado Dado;

int main(int argc, char** argv)
{
    const char* mascara_campos = "%[^,]%*1c%[^,]%*1c%s";
    const char* mascara_dados = "%d,%f,%s";

    Dado        linha;
    FILE* csv = fopen("camera_temp.csv", "r");
    int         n = 0;
    int         quantos = 0;
    char        nome[3][30];

    n = fscanf( csv, mascara_campos,
        &nome[0][0], &nome[1][0], &nome[2][0]);

    printf("fscanf(%d) leu '%s' + '%s' + '%s'\n", n,
        nome[0], nome[1], nome[2]);

    do
    {   // le os caras ate o fim
        n = fscanf(csv,
            mascara_dados,
            &linha.c_series,
            &linha.c_value,
            linha.c_time
            );

        if (n == 3)
        {
            quantos = quantos + 1;
            printf("%3d: %8d %8.1f    %s\n",
                quantos,
                linha.c_series,
                linha.c_value,
                linha.c_time
            );
        }
    } while (n == 3);
    printf("\n%d leituras no arquivo\n", quantos);
    fclose(csv);
    return 0;
};

Está certo?

 

Não. Apesar de mostrar o que está lá. 

 

Num arquivo csv não precisa ter os 3 valores. Tem que aceitar campos com por exemplo

 

Series,Value,Time
1,2.9, teste
,2.9,
,,valido
1,,

Todos esses campos são válidos. Quando o campo está em branco basta ter o delimitador depois para ser considerado válido. Esse troço é importante. Todos os bancos de dados leem CSV, os programas de planilhas e até aquele seu programinha de imprimir etiquetas que vinha em diskette nos anos 90 :)  E esse programa não vai funcionar nesses casos. É só um exemplo.

 

Um arquivo CSV é uma matriz de  valores [x,y] onde x é o número de linhas e y o número de campos descritos na primeira linha. E scanf() falha quando algum campo está vazio.
 

Referência considerada oficial sobre o formato CSV

 

Mas é um começo 

Postado
  Em 02/04/2020 às 02:35, vangodp disse:

O do arfneto fincionou justo porque ele tirou o setlocale e n se deu conta

Expandir  

 

Na verdade não é que eu não me dei conta, @vangodp  :D 

é algo determinado.

 

Como sempre escrevo aqui --- e acho que hoje já escrevi --- não recomendo usar acentos ou locale ou chamar system() ou usar comentários com acentos em programas de console. 

 

Exceto naqueles casos em que eu sempre listo:

  • alguém, está te pagando para fazer isso
  • alguém vai te dar uma boa nota por fazer isso, porque talvez seja o objetivo do programa
  • você tem uma necessidade pessoal ou uma vontade de arrumar problemas e não consegue evitar

Então é claro que não vou usar isso em um programa de exemplo... Que por sinal NÃO funciona direito como eu expliquei...
 

Para os outros casos eu sugiro seguir a recomendação da Microsoft e de todo mundo: use Unicode. E o novo Terminal do Windows. E as rotinas adequadas. E se prepare para ler um pouco... E para uma certa frustração porque nem assim é seguro. Isso só vai dar certo em situações em que tem um parâmetro tipo 

"Content-Type: text/html; charset=UTF-8";

nos arquivos que você recebe. No teclado não tem ;) XML, JSON, e-mail, HTML, aí sim.

 

adicionado 18 minutos depois

corrigindo: nos arquivos que você recebe ou cria com texto em seus programas

Postado
  Em 02/04/2020 às 00:35, arfneto disse:

Para que a struct se só quer contar os registros? E se na  primeira linha estão os nomes dos campos porque não ler afinal? E se o valor  é temperatura (é?) porque não chamar de temperatura, ou value já que é o nome original?  Ninguém leva a sério uma variável chamada temp ou aux afinal :)

Expandir  

Na real essa é a parte três de uma série de exercícios, o primeiro era pra abrir, ler e colocar na tela o conteúdo do texto, a segunda parte é fazer com q ele conte o número de registros no documento e retorne o valor, agora tenho q fazer isso numa struct. Quanto ao nome da variável, eu realmente não considerei muito a respeito :( .

 

  Em 02/04/2020 às 00:35, arfneto disse:

E se dado_t é um ponteiro e você passa o endereço dele para fscanf() tudo que vai conseguir é corromper o próprio valor do ponteiro. Que por sinal não aponta para nada já que você não alocou memória para nenhuma dessas struct....

Expandir  

Eu pensei q deveria ler a quantidade total antes de alocar espaço com o malloc(sizeof(...)).

 

  Em 02/04/2020 às 00:35, arfneto disse:

Podia ter postado ao menos as primeiras linhas do csv direto para poupar trabalho. Afinal apenas as duas primeiras já serviriam. Esse é o sentido de um arquivo csv. A gente precisa da primeira linha para saber o número dos campos e o nome. E depois vem os campos, um registro por linha, um campo entre cada delimitador.

Expandir  

Foi mal, é a primeira vez que utilizo o Clube do Hardware, geralmente, como passo o dia todo na universidade, acabo pedindo ajuda lá, mas a quarentena mudou algumas coisas. Não caio no mesmo erro a próxima vez :)

 

Agradeço mesmo pela força, vou pegar os toques e tentar corrigir.

Conto com sua ajuda na próxima também :D .

adicionado 1 minuto depois
  Em 02/04/2020 às 02:35, vangodp disse:

Esse programa tem um problema algo "imbilito"! O setlocale. Quando você usa setlocale e setea ele para o ptbr, automaticamente todos os floats devem, repito DEVEM, levar vírgula. normalmente fariamos float PI = 3.14, porém depois de setear o locale para ptbr a coisa fica assim: float pi = 3,14;(note a virgula).

Expandir  

Você tem razão, meu professor já tinha me alertado a respeito disso e eu vacilei d novo. Valeu mesmo.

Postado
  Em 02/04/2020 às 20:38, Jony Leal disse:

Eu pensei q deveria ler a quantidade total antes de alocar espaço com o malloc(sizeof(...))

Expandir  

 

Bem... Sim e não. Para saber a quantidade total você em que ler todos, mas ler todos sem gravar só para contar as linhas e depois abrir o arquivo de novo e começar a ler outra vez desta vez gravando. Sim, é possível, mas não parece muito esperto.

Mas você entendeu a ideia.

 

  Em 02/04/2020 às 20:38, Jony Leal disse:

Você tem razão, meu professor já tinha me alertado a respeito disso e eu vacilei d novo. Valeu mesmo

Expandir  

 

Fuja disso a menos que alguém te pague. Não declare nada, Não acentue nada. Apenas faça seu programa. Quando o objetivo for esse, tipo ler caracteres de outros idiomas e tal aí você se preocupa... Meu palpite de sempre.

 

Entendeu o exemplo que te mostrei? Rodou em sua máquina?

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

Mostrar 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

Mostrar mais  
×
×
  • Criar novo...

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

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!