Ir ao conteúdo
  • Cadastre-se

C Inserindo dados em struct de um arquivo .csv com fseek


Leonardo Ely

Posts recomendados

Boa noite galera, estou o dia inteiro tentando fazer a abertura de um arquivo .csv no qual os dados lidos devem ser colocados em uma struct.

Lembrando também que a variável id_twitter tem que ser usada como chave primária, pra não gerar um novo id pra um mesmo usuário que estiver no arquivo csv.

 

Até o momento fiz isso, já vi tudo quanto é vídeo tentei de tudo todo dia e tive progresso já estou saturado, mas vamos lá.

Pra eu registrar um id primeiramente eu teria que preencher um nodo com suas info (usuario,mensagem,data,local) pra que por fim compará-lo com os próximos nodos que forem gerados e não acabar criando um novo id caso um mesmo usuário apareça novamente no arquivo .csv

 

Até o momento entendi que o fseek serve pra setar a posição que irá começar a ler o arquivo (na minha teoria até o momento "0", mas creio não estou correto)

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

struct info{
	char usuario[20];
	char mensagem[280];
	char data [8];
	char local[20];
	int id_twitter;
  
	struct info *prox;
};

void le_dados (FILE *dados)
{

    struct info i;
	
    char *result;


		
        result = fgets(i.usuario, sizeof(i.usuario), dados);

        fseek(dados,0, SEEK_SET);
		fread(&i,sizeof(result),1,dados);
		printf("%s", i.usuario);
            
}

int main( int argc, char ** argv )
{
	setlocale(LC_ALL, "Portuguese");
    FILE * dados = NULL;

    dados = fopen("teste.csv", "rt");

    if (dados == NULL)
    {
        printf("Problemas na criacao do arquivo dados.\n");
        return 0;
    }

    le_dados(dados);
    
	
    fclose(dados);

    return 0;
}

 

Atividade:

Citação

2.1) Registros do Arquivo de Dados:

Twitter: a estrutura do registro a ser utilizado para os dados extraídos da rede social Twitter é composta pelos campos id_twitter (inteiro, chave primária), usuário (caractere de 20 posições), mensagem (caractere de 280 posições), data (caractere de 8 posições), país (caractere de 20 posições) e hashtags (caractere de 200 posições).

Para a implementação dessa funcionalidade deve-se criar um registro de tamanho fixo com o caractere ‘\n’ de final de linha (opcional) e sem separador entre os campos (obrigatório) em uma linguagem de programação (C, C#, C++, Python, PHP, Java ...) que possua o comando seek.

Se outra rede social for a escolhida, estes campos podem ser adequados aos dados extraídos, assim como as demais orientações apresentadas nas demais explicações do trabalho.

 

Organização de Arquivo de Dados:

Criar o registro de dados descrito acima para a organização de arquivo do tipo sequencial usando registro de tamanho fixo com o caractere ‘\n’ de final de linha (opcional) e sem separador entre os campos (obrigatório).

Implementar:

1) um procedimento para inserir as, no mínimo, 10 mil linhas de dados,

2) um procedimento para mostrar os dados,

3) um procedimento para realizar a pesquisa binária e

4) um procedimento para consultar dados a partir da pesquisa binária.

 

Segue o dois arquivos iguais um é .txt e outro é .csv separado por ; porém no exercício foi citado que não pode ter separadores mas era o único jeito que consegui ter algum progresso com meus testes.

Preciso muito de ajuda é serio agradeço desde já!

 

 

 

arquivo-texto.txt csv-separado-virgula.csv

Link para o comentário
Compartilhar em outros sites

Não consegui entender o que tem a ver o arquivo csv com o enunciado que vem depois.

Imagino que já tenha optado por C para escrever isso mas C++ é sempre mais fácil para criar modelos assim.

 

Usando fseek() esse problema é meio trivial. Não há razão para ter um separador. Nada iria acrescentar.

Nos tempos dos mainframes e linguagem COBOL esse formato de arquivo era chamado DAM --- Direct Access Method --- porque afinal é isso mesmo. E afinal os americanos amam abreviaturas de 3 letras.

Um arquivo csv pode ser visto da forma char** ** e também é de tamanho de tamanho fixo, mas em número de campos. Assim converter de csv para DAM e vice-versa é simples.

 

No entanto csv em geral se carrega inteiro para a memória e usa assim. E DAM se acessa um por vez lendo do disco pelo deslocamento. Claro que tem modelos híbridos quando a base de dados é muito grande ou a performance é importante. Coisas como um cache de registros na memória eram comuns com o arquivo em disco e algum tipo de paginação em disco para os csv não seria novidade.

 

De volta ao seu problema

 

Acho que a primeira coisa que precisa é de uma função factory, uma função que monte e devolva um registro aleatório para colocar no arquivo de dados. Uma que sempre reproduza a mesma sequência, mesmo que de milhares de elementos. Isso vai ajudar muito na implantação. Não precisa de registros grandes no início. Eles nada acrescentam. Nem de uma API para pegar dados de qualquer base. Apenas use uma função como eu disse.

 

Que pretende com um ponteiro prox na sua estrutura se o acesso vai ser direto a partir da posição?


Veja essa struct simplificada:
 

typedef struct
{
    int id;

    char usuario[10];
    char mensagem[10];
    char data[10];
    char local[10];

}   Info;

 

Tem 44 bytes por registro apenas e não 332. Depois aumenta os campos para o oficial. Não há sentido em id_twitter ou id_origem porque é apenas um id.

 

A consulta vai ser somente pelo id do twitter e o enunciado é ultra vago, exceto pelo que você não escreveu: 
 

1 hora atrás, Leonardo Ely disse:

as demais orientações apresentadas nas demais explicações do trabalho

 

Que não li. ;) 

 

Mas vamos pensar no simples: 
 

Uma função fábrica --- factory --- declarada talvez assim:

 

    Info* fabrica(int);

 

faz a coisa acontecer:

  • se você chamar com algum número ela reinicia a sequência aleatória e retorna NULL. Importante porque é preciso reproduzir as mensagens mas não queremos baixar ou ler um dump do twitter com 15.000 linhas
  • se chamar com 0 ela retorna uma mensagem completa e preenchida todinha, com 44 ou 332 ou 3400 bytes

Vai claro precisar de uma função de busca binária.

 

O enunciado não pede atualização do arquivo e nada diz sobre a implementação. Eis o que eu faria: o que faz um banco de dados. Definido um máximo de registros a ler, já que só tem um mínimo, cria um vetor de referência para os id. Lê os caras e grava no disco um a um. Ao mesmo tempo grava dois vetores na memória: um por ordem de id e outro por ordem de entrada. Assim não precisa mais de sort ou qualquer tratamento. Leu o arquivo os dados estão prontos.
 

int     pos_id[__AMOSTRA__];
int     id_pos[__AMOSTRA__];

 

O primeiro tem os id por ordem de entrada, como estão no disco.

O segundo tem as posições por ordem de id.

 

Para a entrada por ordem de id o óbvio: usa insertion sort e insere os caras já que vem mesmo um por um. E se fizer uma pesquisa binária nele adivinha o que vai ter? A posição no disco...

O outro vetor será o índice para o deslocamento no arquivo de dados. Assim atenderá a pesquisa por ordem no disco.

 

Exemplo:

 

Assim fica mais fácil de ver: entram 4 registros, id 12, 54, 1 e 3
 

pos_id = { 12, 54, 1, 3 }
id_pos = { 2, 3, 0, 1 }

 

A busca binária e o insertion sort só precisam de uma ajudinha: ao invés de usar a posição se usa a chave na posição. Para isso servem os computadores.

 

Como achar o 54?

 

O segundo vetor dá a chave no disco. O vetor está em ordem de id. O sizeof() dá o tamanho da estrutura. Então se procura o 54 no exemplo a busca binária vai retornar 1 porque a id em pos_id[1] é 54. Então a posição no disco é
1 * sizeof(Info)) e esse é o valor do fseek() para posiconar exatamente nele. Um fread() de sizeof(Info) bytes vai ler a mensagem desse usuário
 

Note que em C++ ou Python ou java poderia usar dois dicionários e não precisaria programar nada quase.

 

Se preferir usar algoritmos convencionais apenas use o segundo vetor com duas colunas e repita o id. Assim não precisa se referir ao primeiro quando estiver navegando por chave

 

Como sempre escrevo, recomendo escrever em torno dos dados e não começar a programar sem uma ideia boa do que está pretendendo fazer
 

O programa abaixo é só um esqueleto com as estruturas

 

Spoiler

#define  __AMOSTRA__ 20000

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

typedef struct
{
    int id;

    char usuario[10];
    char mensagem[10];
    char data[10];
    char local[10];

}   Info;

typedef struct
{
    int id;

    char usuario[20];
    char mensagem[280];
    char data[8];
    char local[20];

}   InfoCompleta;

int     pos_id[__AMOSTRA__];
int     id_pos[__AMOSTRA__];
Info*   fabrica(int);

int main(void)
{
    printf("Tamanho da struct Info: %zd [versao completa: %zd]\n",
        sizeof(Info),
        sizeof(InfoCompleta));
    printf("Tamanho dos vetores: %zdK\n",
        sizeof(pos_id)/1024);
    return 0;
};

Info* fabrica(int reset)
{
    if (reset != 0)
    {
        srand(reset);
        return NULL;
    };
    // gera um registro
    Info* novo = NULL;
    return novo;
};

 

 

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

6 horas atrás, Leonardo Ely disse:

separado por ; porém no exercício foi citado que não pode ter separadores

 

Que coisa confusa. Supõe-se que um formato .csv é exatamente separado por vírgulas (comma em inglês, por isso um "c" em "csv"). Quando você se refere a não ter separador, significa ter um espaço em branco entre as entradas? tipo, "aaaa bbbb cccc dddd"? ou nem isso, ou seja, "aaaabbbbccccdddd"?

 

Caso tenha um espaço em branco, no fim das contas há um separador. Bastaria então usar a função getline (da biblioteca stdio.h) pra ler uma linha inteira, passar essa linha em seguida pra função strtok que a dividiria nas respectivas entradas (usando o espaço em branco como delimitador), e copiar cada uma pra sua respectiva string em struct info. Depois repetiria o processo pra próxima linha do arquivo.

 

Há uma infinidade de soluções possíveis dependendo do formato do arquivo. Caso não tenha um espaço em branco mas sempre tenha uma característica usável, serve também. Por exemplo, o nome do usuário sempre começa com @, a mensagem sempre começa com " etc.

 

Em tempo, isso aqui tá errado aparentemente: 

 

6 horas atrás, Leonardo Ely disse:

fread(&i,sizeof(result),1,dados);

 

Você deu o endereço de um objeto, &i, do tipo struct info, pra guardar 1 entrada de sizeof(result) bytes, sendo que result é um ponteiro pra char e não corresponde ao tamanho de i. Uhh! Provavelmente você tinha uma ideia e escreveu outra coisa sem querer.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

6 horas atrás, V!OLADOR disse:

Que coisa confusa. Supõe-se que um formato .csv é exatamente separado por vírgulas (comma em inglês, por isso um "c" em "csv"). Quando você se refere a não ter separador, significa ter um espaço em branco entre as entradas? tipo, "aaaa bbbb cccc dddd"? ou nem isso, ou seja, "aaaabbbbccccdddd"

 

Me intrometendo na questão: leu o enunciado? As API de extração de dados em grau entregam os dados em três possíveis formatos: csv, JSON e XML. 

 

Veja uma linha da entrada fornecida pela API do twitter, e pelo autor afinal, a terceira linha

 

#Eleições2020 #Irara";21/10/2020 00:12;Salvador
italoguedes50;"Equipe de Comunicação

 

Trata-se de um csv, o estabelecido formato que precede a internet. Os campos não estão protegidos. Acredito que seja uma opção (errada) de quem extraiu os dados. Falta o header. que o usuário não mostrou ou também não foi extraído. 

 

Um header para esse caso seria 
 

id;usuario; mensagem; data; local;

 

mas acho que o autor extraiu errado afinal, como vai ficar claro mais abaixo: ao invés de local vem o país e as hashtags.

 

6 horas atrás, V!OLADOR disse:

Quando você se refere a não ter separador, significa ter um espaço em branco entre as entradas? tipo, "aaaa bbbb cccc dddd"? ou nem isso, ou seja, "aaaabbbbccccdddd"

 

Pelo enunciado, a opção "nem isso". Leu o que eu expliquei sobre arquivos DAM dos '80? Sei que não sou o autor da pergunta, só tentei dar um exemplo. É o objetivo do projeto.
 

Sobre o separador, direto do enunciado:


No item 

 

Sobre os campos 

 

12 horas atrás, Leonardo Ely disse:

Organização de Arquivo de Dados


Sobre os Registros

 

12 horas atrás, Leonardo Ely disse:

registro de tamanho fixo com o caractere ‘\n’ de final de linha (opcional)

12 horas atrás, Leonardo Ely disse:

sem separador entre os campos (obrigatório)

 

A opção pelo separador de linha é para se o aluno preferir usar arquivos de texto para ler, como xxx.readlines() em Python. Mas não precisa disso em C ou C++.

 

6 horas atrás, V!OLADOR disse:

Há uma infinidade de soluções possíveis dependendo do formato do arquivo. Caso não tenha um espaço em branco mas sempre tenha uma característica usável, serve também. Por exemplo, o nome do usuário sempre começa com @, a mensagem sempre começa com " etc.

 

Acho que os formatos já estão bem estabelecidos. Falta o formato de indexação, as estruturas de dados. Uma solução pode ser a que mostrei acima. E a linguagem poderia ser outra e usar dicionários ou mapas ou mesmo listas e vetores em C++. Ou java. Ou Python. Ou COBOL  e DAM ;) 

 

@V!OLADORTem razão quanto ao programa ter erros, eu acho. Mas também acho que o programa é só um placeholder para os moderadores não interceptarem a questão facilmente, o popular tapa-buraco

 

Resumo

 

O programa abre um arquivo csv extraído do twitter ou outra rede social, com ao menos 10 mil registros, e gera um arquivo binário com registros  de tamanho fixo. E implementa pesquisas por id e posição no disco.


Talvez tenha problemas por não ter usado o caracter de "escape" na extração. A mensagem é de texto comum, então ou (a) você protege --- delimita --- os campos ou (b) protege os delimitadores no conteúdo. (a) é o comum e esperto. (b) seria meio ingênuo. Esses arquivos costumam ter dezenas ou centenas de milhares de registros. 

 

Veja um comando comum de extração de um csv
 

LOAD DATA INFILE '/home/export_file.csv'
   INTO TABLE table_name
   FIELDS TERMINATED BY ';'
   ENCLOSED BY '"'
   LINES TERMINATED BY '/n'
   IGNORE 1 ROWS;

 

E acho que dá pra entender a história toda. A extração não pode dar erro. Esse formato é anterior a essas linguagens e bancos de dados e planilhas e redes , o XML dos '70. Esse é o comando do banco de dados mySQL em 2020. Mas podia ser do Excel ou do Oracle.

 

No exemplo o csv deveria estar assim
 

usuario;mensagem;data;pais;hashtags
"@idTwitter";"Mensagem ; ; ; ; ";"20/20/2020";"Austria";"#teste #hash1 #forum #clube"

 

"IGNORE 1 ROWS" pula o header onde tem os nomes dos campos. Se usa quando a tabela já tem nomes para as colunas e não vai usar esses. Numa planilha já coloca esses nomes no título de cada coluna. 
LINES TERMINATED BY é o claro: nem sempre o fim de registro é o newline

ENCLOSED BY permite que os campos possam ter ocorrências do delimitador
 

Veja o resultado de importar esse exato csv no Planilhas do Google:
 

image.png.e30542990f938838b0a3f92d054a46e6.png

 

E veja que o programa percebeu que os campos eram delimitados por aspas, tirou as aspas e deixou um campo cheio de '; ' que era o separador do csv...

 

Espero que isso ajude a entender a mecânica dessas coisas

 

Tem muita gente fazendo isso ou pagando por isso hoje em dia, para usar em inteligência artificial e data mining. Mas em geral se usam bibliotecas prontas. Python tem um módulo csv por exemplo.

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

5 horas atrás, arfneto disse:

Me intrometendo na questão: leu o enunciado? As API de extração de dados em grau entregam os dados em três possíveis formatos: csv, JSON e XML.

 

Saudações, @arfneto! Fique a vontade pra entrometer-se sempre. Não li tudo em detalhes mas concordei com tudo que vi. Meu ponto entretanto era outro. O post original não tá bem redigido e deixa várias duvidas. Pra citar um exemplo:

 

Dando uma olhadela rápida no arquivo .txt do @Leonardo Ely, o cabeçalho tem essa cara:

 

usuario	mensagem	data	local

 

Com um espaço entre "usuario" e "mensagem" e tabs entre o resto (provavelmente uma exportação direta de um MS Excel ou algo do gênero).

 

Na primeira linha temos:

 

HarissonAndrade	"@arthurmoledoval e @GuilhermeBoulos quais sao... #Eleicoes2020"	21/10/2020 00:12	Brasil

 

Incompatível com o cabeçalho caso horário não seja considerado data (o que não parece ser o caso) e com separadores evidentes. Então, além das inconsistências, se ele utiliza um .txt com formatação diferente do .csv pra desenhar o algoritmo, o trabalho torna-se um pesadelo (curiosamente, no post original o .txt e .csv são classificados como idênticos, como de fato espera-se).

 

Então não me refiro ao que eu gostaria que fosse ou meu estilo de .csv favorito e sim ao problema especifico do @Leonardo Ely, cuja descrição é um pouco incoerente. Veja que todas essa informações inconsistentes foram dadas no post original ao invés da enfase no formato que ele quer efetivamente utilizar:

 

HarissonAndrade"@arthurmoledoval e @GuilhermeBoulos quais sao... #Eleicoes2020"21/10/202000:12Brasil

 

E nem isso ele confirmou. O que me leva ao meu post inicial: caso especifique (coerentemente, precisamente) um formato, o problema é trivial.

 

Em programação, a precisão na descrição do problema, e da solução, é um princípio fundamental.

  • Curtir 2
Link para o comentário
Compartilhar em outros sites

Sinceramente consigo entender tudo que vocês me explicam e o que a princípio eu teria que fazer mas na hora de implementar não consigo fazer nada fico parado e bloqueado.
 

Mas me diz dentro dessa parte que eu deveria fazer a abertura do arquivo? Onde quando for NULL inserir os primeiros dados e quando for diferente de NULL inserir o resto? 

16 horas atrás, arfneto disse:

Info* fabrica(int reset)
{
        if (reset != 0)
        {
          srand(reset);
          return NULL;
        };
// gera um registro
       Info* novo = NULL;
       return novo;
};

 

Link para o comentário
Compartilhar em outros sites

17 horas atrás, arfneto disse:

O programa abaixo é só um esqueleto com as estruturas

 

Esse é um parágrafo importante. Talvez eu devesse ter explicado melhor no entanto. Vou postar uma função assim porque eu acho que é importante.

 

Eu não programei nada. Só escrevi o programa para ter onde ver os tamanhos e postar os exemplos. Não é funcional. Vou te mostrar uma função dessas depois, no café da tarde ;) aqui

 

1 hora atrás, V!OLADOR disse:

E nem isso ele confirmou. O que me leva ao meu post inicial: caso especifique (coerentemente, precisamente) um formato, o problema é trivial

 

1 hora atrás, V!OLADOR disse:

Então não me refiro ao que eu gostaria que fosse ou meu estilo de .csv favorito e sim ao problema especifico do @Leonardo Ely, cuja descrição é um pouco incoerente. Veja que todas essa informações inconsistentes foram dadas no post original ao invés da enfase no formato que ele quer efetivamente utilizar

 

Não há tipos de csv. Esse formato é tão antigo que sequer tem um padrão, porque IETF ou W3C por exemplo só apareceram muito depois. E o padrão já estava tão estabelecido que não havia razão para discutir e gastar tempo e dinheiro com isso. Não foi o caso com XML e os namespaces por exemplo, décadas depois.

 

Há um documento aceito como referência no próprio IETF: RFC 4180 e que explica isso, apesar de não haver necessidade: está incorporado com sucesso em todo lugar. Como por exemplo em API de extração de dados de rede social.

 

Eu acho que os dados foram extraídos com erro simplesmente, como mostrei no tópico anterior.

 

Isso seria o correto:
 

usuario;mensagem;data;pais;hashtags
"@idTwitter";"Mensagem ; ; ; ; ";"20/20/2020";"Austria";"#teste #hash1 #forum #clube"

 

Por exemplo importado assim pelo Planilhas, do Google

image.png

 

O propósito do programa é transformar o arquivo csv da entrada em um arquivo sequencial de acesso direto em disco, usando um índice e fseek(). E implementar as funções de pesquisa por posição ou por id.

 

37 minutos atrás, Leonardo Ely disse:

Sinceramente consigo entender tudo que vocês me explicam e o que a princípio eu teria que fazer mas na hora de implementar não consigo fazer nada fico parado e bloqueado

 

Leu tudo que eu disse?

Entendeu que o csv está errado? Entendeu sobre as tabelas de referência, os dois vetores? 

 

Programou algo mais? A busca binária ao menos?

 

Vou adaptar uma função que eu escrevi para um pessoal aqui e depois eu posto, no caso das "factory functions" :) 

Link para o comentário
Compartilhar em outros sites

@arfneto

Citação

Leu tudo que eu disse?

Li sim, entendi que é só uma noção prévia o código que tu mandou mais acima.
 

 

Citação

Entendeu que o csv está errado?

Não entendi realmente ainda o que tem de errado no arquivo csv.

 

Citação

Entendeu sobre as tabelas de referência, os dois vetores? 

Entendi sim sobre os dois vetores, como foi falado um irá por ordem de entrada de id e outro irá ordenadar os ids por insertion sort.

 

Citação

Programou algo mais? A busca binária ao menos?

Não ainda não programei mais como falei estou meio sei lá não sei realmente como começar, parece que todas maneiras que faço 10% está no caminho e o resto é desnecessário, as aulas como no momento estão sendo online e só sendo basicamente enfiado conteúdo teórico goela a baixo sem exemplos ao vivo de implementação está sendo quase impossível de absorver algo (quando se trata de programar), mas não vem ao caso isso, só que já estou desistindo.

 

Citação

Como sempre escrevo, recomendo escrever em torno dos dados e não começar a programar sem uma ideia boa do que está pretendendo fazer

Esse é o maior problema como só sei programar em C e SQL(que não se cabe no exercício) no momento ainda tenho muita dificuldade quando aprofundasse um pouco na linguagem, eu sei que consigo mas sem um auxílio de alguem experiente não consigo fazer fluir, as vezes uma vírgula atrapalha assim como está função:

 

17 horas atrás, arfneto disse:

Info* fabrica(int reset)
{
};

Utilizei ela uma vez só nesse formato de um exemplo e me deu uma tremenda dor de cabeça por ser diferente da maneira que eu faço, quando tentei implementar ela em outros exercícios então não sabia nem onde eu estava. Me atrapalho fácil assim..


Qual a diferença da citada acima pra uma função normal com void fabrica(int reset){}; ?...

Segue o exercício completo que não mandei pra entender melhor

Trabalho I - 2020-4 - Arquivos Sequenciais Indexados (5).pdf

Link para o comentário
Compartilhar em outros sites

5 minutos atrás, Leonardo Ely disse:

Não entendi realmente ainda o que tem de errado no arquivo csv

 

Apenas compare o arquivo que te mostrei com o que está usando.

 

De onde vieram esses dados do csv? acho que foram extraídos com erro. Onde está o arquivo com "ao menos 10 mil linhas" que vai usar? Quem vai fornecer isso?

 

Sabe como funciona essa extração de dados do tweeter? Tem que ter uma identificação de desenvolvedor.

 

7 minutos atrás, Leonardo Ely disse:

Não ainda não programei mais como falei estou meio sei lá não sei realmente como começar, parece que todas maneiras que faço 10% está no caminho e o resto é desnecessário, as aulas como no momento estão sendo online e só sendo basicamente enfiado conteúdo teórico goela a baixo sem exemplos ao vivo de implementação está sendo quase impossível de absorver algo (quando se trata de programar), mas não vem ao caso isso, só que já estou desistindo

 

Programe coisas pequenas do projeto. Não desista.Por exemplo:

  • Crie e leia um arquivo do disco. Tipo 10 registros de Info e depois um loop para ler pelo número
  • Crie um vetor de 10 int tipo 10 pares e programe a busca binaria por um int
  • Crie um programa que le o csv de duas linhas que te mostrei

Não desista

 

Se continuar posso ajudar, como outros aqui. E pode conseguir ajuda em livros, sites, sua escola. Se desistir não vai conseguir nada.

 

11 minutos atrás, Leonardo Ely disse:

Esse é o maior problema como só sei programar em C e SQL(que não se cabe no exercício) no momento ainda tenho muita dificuldade quando aprofundasse um pouco na linguagem, eu sei que consigo mas sem um auxílio de alguem experiente não consigo fazer fluir, as vezes uma vírgula atrapalha assim como está função

 

SQL tem 7 comandos e é uma linguagem de consulta. Com certeza poderia usar aqui afinal o enunciado não exclui SQL como linguagem, apesar de ser apenas de consulta. Mas pouco ganharia

 

Se usar SQL para extrair os dados do csv ainda não vai a lugar algum. O objetivo do programa é o arquivo binário.

 

13 minutos atrás, Leonardo Ely disse:

Qual a diferença da citada acima pra uma função normal com void fabrica(int reset){};

 

A diferença é total. Não use void para nada. Em geral é um desperdício e muitas vezes um erro. Aqui seria um erro. Uma função do tipo que expliquei --- factory, fábrica --- retorna algo novo. No caso uma mensagem do twetter.

Uma função void fabrica(int) não retorna nada. Então não fabrica nada. Apesar de que poderia criar um arquivo ou algo assim.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

7 minutos atrás, arfneto disse:

De onde vieram esses dados do csv?

Esse é o link que consegui todos dados: por busca principal a hashtag #eleicoes2020
https://docs.google.com/spreadsheets/d/1H8lN1Y13MOhgTHc_lt7-sGyy9JWf-MeEeKzb7eH7s-M/copy


No vídeo abaixo ensina como extrair facilmente, no caso eu peguei copiei e joguei no excel talvez tenha sido esse o problema, pois o site oferece a opção de exportar diretamente no computador.


No anexo tem 15 mil registros que peguei do link ali em cima.

 

7 minutos atrás, arfneto disse:

Crie e leia um arquivo do disco. Tipo 10 registros

Sim por isso mostrei só 5 registros, porém estava da maneira incorreta.

7 minutos atrás, arfneto disse:

Uma função void fabrica(int) não retorna nada. Então não fabrica nada. Apesar de que poderia criar um arquivo ou algo assim

Ok

pronto_excel.xlsx

Link para o comentário
Compartilhar em outros sites

21 horas atrás, Leonardo Ely disse:

Segue o dois arquivos iguais um é .txt e outro é .csv separado por ; porém no exercício foi citado que não pode ter separadores mas era o único jeito que consegui ter algum progresso com meus testes.

Preciso muito de ajuda é serio agradeço desde já!

 

 

 

arquivo-texto.txt 847 B · 3 downloads csv-separado-virgula.csv 847 B · 2 downloads

Parece que você só copiou e colou os dados do xls nesses arquivos texto.

 

Mas acho que não deve ser assim, como o exercício pede para criar um arquivo no formato fixo você teria que trabalhar esses dados para o formato especificado.

 

Por exemplo, se pegar os 4 primeiros dados da planilha, entendo que deveriam ficar p.ex no arquivo texto assim,

 

0001     HarissonAndrade@arthurmol10/21/20              Brasil
0002          EletromitoSó Irará t10/21/20            Salvador
0003       italoguedes50Equipe de 10/21/20 João Pessoa, Brasil
0004              otempoVeja vídeo10/21/20       Contagem / MG

 

Isso é só um exemplo de formato fixo e para facilitar a visualização deixei os campos menores: 4 para chave, 20 usuário, 10 mensagem, 8 data e 20 local. Assim no formato fixo seu programa vai ler os dados com base no tamanho dos campos.

  • Curtir 2
Link para o comentário
Compartilhar em outros sites

2 minutos atrás, Midori disse:

Parece que você só copiou e colou os dados do xls nesses arquivos texto.

Sim na verdade salvei um em .csv separado por vírgula (única opção que tinha em .csv do excel) e salvei de novo do arquivo principal do excel em .txt (separado por espaços como dizia o programa)

 

Citação

 entendo que deveriam ficar p.ex no arquivo texto assim

Não entendi o que quis dizer

registros_twitter - Archive.csv
Gerei outros registros no link que citei:

58 minutos atrás, Leonardo Ely disse:

Agora exportei diretamente dali, só que as letras especiais não reconhecem (não é um problema), mas está tudo separado por virgula agora.

Link para o comentário
Compartilhar em outros sites

4 minutos atrás, Midori disse:

Isso é só um exemplo de formato fixo e para facilitar a visualização deixei os campos menores: 4 para chave, 20 usuário, 10 mensagem, 8 data e 20 local. Assim no formato fixo seu programa vai ler os dados com base no tamanho dos campos

 

Isso. E depois ,através dos índices, você acessa diretamente o registro no disco usando o índice e o tamanho do registro. Assim pode trabalhar com milhões de registros usando apenas um registro na memória. Trata-se do modelo que eu expliquei num tópico anterior: arquivo de dados e arquivo de índices. Com o índice na memória. Como todo banco de dados. Como o DAM do COBOL nos mainframes nos '70.

 

13 minutos atrás, Leonardo Ely disse:

Sim na verdade salvei um em .csv separado por vírgula (única opção que tinha em .csv do excel) e salvei de novo do arquivo principal do excel em .txt (separado por espaços como dizia o programa)

 

Você precisa do arquivo csv, mas com os campos de seu exercício, como te mostrei no csv acima... Importar para uma planilha é só para segurança. Mas pode usar a planilha para selecionar os campos de que precisa e gravar em outro csv para seu programa consumir...
 

2 horas atrás, arfneto disse:

usuario;mensagem;data;pais;hashtags
"@idTwitter";"Mensagem ; ; ; ; ";"20/20/2020";"Austria";"#teste #hash1 #forum #clube"

 

 

E pode testar como te mostrei acima, no Planilhas.

 

Entenda que esse arquivo deve ter os campos protegidos, por aspas por exemplo, e expliquei isso no primeiro tópico: se não fizer isso e a mensagem tiver ';' no meio, que é o seu delimitador, não vai conseguir importar os dados. Ou vai ficar absurdamente difícil até você ver porque isso foi feito assim nos anos 70 e proteger os campos

 

Claro que não precisa da primeira linha no programa. A dos headers. Mas na planilha ajuda bem. E eu expliquei isso quando mostrei o comando do mySQL. Como já usou SQL imagino que entenda do que estou falando.

 

C não é uma boa escolha para escrever isso, mas entendo que só conhece C e SQL então assim será, em C. 

 

Pelo enunciado sua estrutura tem que ter 
 

typedef struct
{
    int id;

    char usuario[20];
    char mensagem[280];
    char data[8];
    char pais[20];
    char hashtags[200];

}   Info;
21 horas atrás, Leonardo Ely disse:

2.1) Registros do Arquivo de Dados:

Twitter: a estrutura do registro a ser utilizado para os dados extraídos da rede social Twitter é composta pelos campos id_twitter (inteiro, chave primária), usuário (caractere de 20 posições), mensagem (caractere de 280 posições), data (caractere de 8 posições), país (caractere de 20 posições) e hashtags (caractere de 200 posições).

 

A data no tweeter tem hora e minuto no final. Vai ter que fazer algo na importação do csv

 

Um exemplo:
 

Você sabe que vai precisar ver mensagens no formato do enunciado, então considere
 

#define  __AMOSTRA__ 20000

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

typedef struct
{
    int id;

    char usuario[21];
    char mensagem[281];
    char data[9];
    char pais[21];
    char hashtags[201];

}   Info;

int     pos_id[__AMOSTRA__];
int     id_pos[__AMOSTRA__];

Info*   fabrica(int);
int     mostra_um(Info*);

 

mostra_um() mostra na tela o conteúdo de uma mensagem, imaginando que possa usar um byte dentro de cada campo durante os testes apenas para facilitar... São 5 bytes a mais :) 

 

Uma possível função
 

int     mostra_um(Info* um)
{
    printf("\n=====>'id: '%d'\n\n", um->id);
    printf("\t%10s: ..... '%s'\n", "Usuario", um->usuario);
    printf("\t%10s: ..... '%s'\n", "mensagem", um->mensagem);
    printf("\t%10s: ..... '%s'\n", "data", um->data);
    printf("\t%10s: ..... '%s'\n", "pais", um->pais);
    printf("\t%10s: ..... '%s'\n\n\n", "hashtags", um->hashtags);
    return 1;
};

 

Que poderia mostrar para um possível registro

 

=====>'id: '42'

           Usuario: ..... 'Usuario Exemplo'
          mensagem: ..... 'Mensagem'
              data: ..... '1112019'
              pais: ..... 'WonderLand'
          hashtags: ..... '#teste #um #dois'

 

E então pode usar num programa assim:
 

int main(void)
{
    Info exemplo =
    {
        .id = 42,
        .usuario = "Usuario Exemplo",
        .mensagem = "Mensagem",
        .data = "1112019",
        .pais = "WonderLand",
        .hashtags = "#teste #um #dois"
    };
    printf("Tamanho da struct Info: %zd\n",
        sizeof(Info));
    mostra_um(&exemplo);
    return 0;
};

 

E já pode seguir com seu programa...

 

Eis o exemplo compilável

 

Spoiler

#define  __AMOSTRA__ 20000

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

typedef struct
{
    int id;

    char usuario[21];
    char mensagem[281];
    char data[9];
    char pais[21];
    char hashtags[201];

}   Info;

int     pos_id[__AMOSTRA__];
int     id_pos[__AMOSTRA__];

Info*   fabrica(int);
int     mostra_um(Info*);

int main(void)
{
    Info exemplo =
    {
        .id = 42,
        .usuario = "Usuario Exemplo",
        .mensagem = "Mensagem",
        .data = "1112019",
        .pais = "WonderLand",
        .hashtags = "#teste #um #dois"
    };
    printf("Tamanho da struct Info: %zd\n",
        sizeof(Info));
    mostra_um(&exemplo);
    return 0;
};

Info* fabrica(int reset) { return NULL; };

int     mostra_um(Info* um)
{
    printf("\n=====>'id: '%d'\n\n", um->id);
    printf("\t%10s: ..... '%s'\n", "Usuario", um->usuario);
    printf("\t%10s: ..... '%s'\n", "mensagem", um->mensagem);
    printf("\t%10s: ..... '%s'\n", "data", um->data);
    printf("\t%10s: ..... '%s'\n", "pais", um->pais);
    printf("\t%10s: ..... '%s'\n\n\n", "hashtags", um->hashtags);
    return 1;
};

 

 

Para começar a testar suas coisas, pro exemplo gerando o arquivo em disco. Se tiver a tal função fábrica que pode repetir os dados sempre, já pode escrever toda a parte de saída do programa...

 

E a tal "fabrica"? Para que serviria?

 

Quando é preciso ter algo logo para um protótipo, dados reais reprodutíveis em qualquer escala, é o normal. Assim se pode terminar logo o protótipo e saber se está no caminho certo, ou mesmo se qualificar para um projeto ou receber um cheque de adiantamento ;) , ou entregar um trabalho chato no mesmo dia.

 

Imagine que existam
 

Info*   fabrica(int);
int     mostra_um(Info*);

 

Isso quer dizer que você pode testar seu programa e ver os dados para 100.000 registros agora mesmo, sem arquivo e sem depender de ninguém. Algo assim:
 

int main(void)
{
    printf("Tamanho da struct Info: %zd\n",
        sizeof(Info));

    Info* teste = fabrica(20201021); // reset

    // gera 3 registros so pra ver

    for (int i = 0; i < 4; i += 1)
    {
        teste = fabrica(0);
        mostra_um(teste);
        // nao precisa mais desse
        free(teste);
    };

    return 0;
};

 

Gera 3 registros e mostra na tela. Agora se você está fazendo esse trabalho em grupo e um pessoal vai ler o csv e gravar em disco e outro, você por exemplo, vai gravar em disco e fazer as pesquisas e a busca binária, vai ficar esperando que eles terminem para você começar?

 

Espero que tenha entendido: se trocar aquele 4 no exemplo por 40.000 e gravar em disco antes de apagar vai ter o arquivo binario para testar as pesquisas. Com dados completos. Depois você implementa os índices e a parte de manter a id única. O que pode fazer como fazia em SQL: auto increment :) 

 

Veja um resultado de um programa assim, usando  fabrica()

 

Tamanho da struct Info: 536

=====>'id: '6089'

           Usuario: ..... 'Usuario 27474'
          mensagem: ..... 'Mensagem 15822'
              data: ..... '25062018'
              pais: ..... 'Pais 15115'
          hashtags: ..... '#teste #hashum #zero'



=====>'id: '5542'

           Usuario: ..... 'Usuario 26045'
          mensagem: ..... 'Mensagem  1908'
              data: ..... '15102018'
              pais: ..... 'Pais 10633'
          hashtags: ..... '#valor #hash #tres'



=====>'id: '241'

           Usuario: ..... 'Usuario 21699'
          mensagem: ..... 'Mensagem 11531'
              data: ..... '15032018'
              pais: ..... 'Pais 27489'
          hashtags: ..... '#teste #hashum #dois'



=====>'id: '3071'

           Usuario: ..... 'Usuario  8451'
          mensagem: ..... 'Mensagem 13115'
              data: ..... '09072019'
              pais: ..... 'Pais  4981'
          hashtags: ..... '#teste'

 

E toda vez que rodar com o mesmo valor de reset vai gerar as mesmas mensagens então pode testar até funcionar.

Sem mudar uma linha na entrada.

 

Como seria uma função dessas?

 

Como te expliquei, a mecânica está no programa, Pode ser simples assim:
 

Info* fabrica(int reset)
{
    // prototipos para as hastags
    const static char* tags[5] =
    {
        { "#teste #hashum #zero"},
        { "#teste"},
        { "#teste #hashum #dois"},
        { "#valor #hash #tres"},
        { "#hashum #zero"}
    };
    static char linha[50];
    if (reset != 0)
    {
        srand(reset);
        return NULL;
    };
    // cria um novo Info
    Info* novo = (Info*)malloc(sizeof(Info));
    // preenche
    novo->id = rand() % 10000;
    sprintf(novo->usuario, "Usuario %5d", rand());
    sprintf(novo->mensagem, "Mensagem %5d", rand());
    // data
    sprintf(novo->data, "%02d%02d%04d", 
        1+rand()%28, 1+rand()%12, 2018+rand()%2);
    // pais
    sprintf(novo->pais, "Pais %5d", rand());
    // ecolhe um dos 5 conjuntos de hashtags
    sprintf(novo->hashtags, "%s", tags[rand()%5]);
    return novo;
};  // fabrica()


Nada mais.

 

O programa que gerou isso:

Spoiler

#define  __AMOSTRA__ 20000

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

typedef struct
{
    int id;

    char usuario[20];
    char mensagem[280];
    char data[10];
    char pais[20];
    char hashtags[200];

}   Info;

int     pos_id[__AMOSTRA__];
int     id_pos[__AMOSTRA__];

Info*   fabrica(int);
int     mostra_um(Info*);

int main(void)
{
    printf("Tamanho da struct Info: %zd\n",
        sizeof(Info));

    Info* teste = fabrica(20201021); // reset

    // gera 3 registros so pra ver

    for (int i = 0; i < 4; i += 1)
    {
        teste = fabrica(0);
        mostra_um(teste);
        // nao precisa mais desse
        free(teste);
    };

    return 0;
};

Info* fabrica(int reset)
{
    // prototipos para as hastags
    const static char* tags[5] =
    {
        { "#teste #hashum #zero"},
        { "#teste"},
        { "#teste #hashum #dois"},
        { "#valor #hash #tres"},
        { "#hashum #zero"}
    };
    static char linha[50];
    if (reset != 0)
    {
        srand(reset);
        return NULL;
    };
    // cria um novo Info
    Info* novo = (Info*)malloc(sizeof(Info));
    // preenche
    novo->id = rand() % 10000;
    sprintf(novo->usuario, "Usuario %5d", rand());
    sprintf(novo->mensagem, "Mensagem %5d", rand());
    // data
    sprintf(novo->data, "%02d%02d%04d", 
        1+rand()%28, 1+rand()%12, 2018+rand()%2);
    // pais
    sprintf(novo->pais, "Pais %5d", rand());
    // ecolhe um dos 5 conjuntos de hashtags
    sprintf(novo->hashtags, "%s", tags[rand()%5]);
    return novo;
};  // fabrica()

int     mostra_um(Info* um)
{
    printf("\n=====>'id: '%d'\n\n", um->id);
    printf("\t%10s: ..... '%s'\n", "Usuario", um->usuario);
    printf("\t%10s: ..... '%s'\n", "mensagem", um->mensagem);
    printf("\t%10s: ..... '%s'\n", "data", um->data);
    printf("\t%10s: ..... '%s'\n", "pais", um->pais);
    printf("\t%10s: ..... '%s'\n\n\n", "hashtags", um->hashtags);
    return 1;
};

 

 

E pode começar a testar em 15 minutos. Esse programa ou o próximo.

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

@arfneto Agradeço desde já pelo último código de exemplo tudo já ficou mais facilitado pra mim entender mas ainda falta uma caminhada.

 

Só deixa eu tentar entender mais claramente na função fábrica..
 

Você pode me explicar mais detalhado?
Por exemplo o novo->id = rand () %  10000 pega um numero aleatorio dos 10000 registros e poe um id, mas isso não tem como se repetir se for pegar todas as 10000 posições?  E tambem pensando na ordenação se fosse fazer cada vez que passase por aí uma variavel int auxiliar, não poderia ser feito novo->id = auxiliar + 1? Evitando ter que ordenar os ids? Pois já taria ordenando os ids em ordem.. É claro que dentro disso terá que ser feito outra parte para não repetir um mesmo usuário aí seria registrada uma outra mensagem com o mesmo id.. Está certo a linha de raciocínio?

 

Info* fabrica(int reset)
{

    // prototipos para as hastags
    const static char* tags[5] =
    {
        { "#teste #hashum #zero"},
        { "#teste"},
        { "#teste #hashum #dois"},
        { "#valor #hash #tres"},
        { "#hashum #zero"}
    };
    int a;
    static char linha[50];
    if (reset != 0)
    {
        srand(reset);
        return NULL;
    };
    // cria um novo Info
    Info* novo = (Info*)malloc(sizeof(Info));
    // preenche
    
    novo->id = rand() % 10000;
    
    sprintf(novo->usuario, "Usuario %5d", rand());
    sprintf(novo->mensagem, "Mensagem %5d", rand());
    // data
    sprintf(novo->data, "%02d%02d%04d", 
        1+rand()%28, 1+rand()%12, 2018+rand()%2);
    // pais
    sprintf(novo->pais, "Pais %5d", rand());
    // ecolhe um dos 5 conjuntos de hashtags
    sprintf(novo->hashtags, "%s", tags[rand()%5]);
    return novo;
};  // fabrica()

 

 

Link para o comentário
Compartilhar em outros sites

2 horas atrás, Leonardo Ely disse:

Por exemplo o novo->id = rand () %  10000 pega um numero aleatorio dos 10000 registros e poe um id, mas isso não tem como se repetir se for pegar todas as 10000 posições?  E tambem pensando na ordenação se fosse fazer cada vez que passase por aí uma variavel int auxiliar, não poderia ser feito novo->id = auxiliar + 1? Evitando ter que ordenar os ids? Pois já taria ordenando os ids em ordem.. É claro que dentro disso terá que ser feito outra parte para não repetir um mesmo usuário aí seria registrada uma outra mensagem com o mesmo id.. Está certo a linha de raciocínio?

 

Está certo o raciocínio mas é irrelevante nessa hora. O objetivo aqui é simplesmente criar e testar a parte de gravação e pesquisa do arquivo. no mesmo dia em que começou a escrever o programa, por exemplo. Escrever uma função assim leva 10 minutos, nem isso. E sempre funciona.

 

5 horas atrás, arfneto disse:

Espero que tenha entendido: se trocar aquele 4 no exemplo por 40.000 e gravar em disco antes de apagar vai ter o arquivo binario para testar as pesquisas. Com dados completos. Depois você implementa os índices e a parte de manter a id única. O que pode fazer como fazia em SQL: auto increment

 

Leu essa parte acima quando eu escrevi um pouco antes? É isso.

 

Sabe o que é auto increment no SQL ? Então veja
 

Info* fabrica(int reset)
{

    // prototipos para as hastags
    const static char* tags[5] =
    {
        { "#teste #hashum #zero"},
        { "#teste"},
        { "#teste #hashum #dois"},
        { "#valor #hash #tres"},
        { "#hashum #zero"}
    };
    static int id = 1023;
    int a;
    static char linha[50];
    if (reset != 0)
    {
        srand(reset);
        return NULL;
    };
    // cria um novo Info
    Info* novo = (Info*)malloc(sizeof(Info));
    // preenche
    id += 1;
    novo->id = id;
    
    sprintf(novo->usuario, "Usuario %5d", rand());
    sprintf(novo->mensagem, "Mensagem %5d", rand());
    // data
    sprintf(novo->data, "%02d%02d%04d", 
        1+rand()%28, 1+rand()%12, 2018+rand()%2);
    // pais
    sprintf(novo->pais, "Pais %5d", rand());
    // ecolhe um dos 5 conjuntos de hashtags
    sprintf(novo->hashtags, "%s", tags[rand()%5]);
    return novo;
};  // fabrica()

 

uma versão de fabrica() com auto incremento: mudança? 3 linhas... Não deve perder tempo com esses detalhes desde o início. Isso é um erro muito comum. Às vezes só de conseguir um protótipo rápido todo mundo já pode trabalhar no resultado. Isso faz você entregar seu trabalho antes. Mas também faz você conseguir um trabalho antes. Pegar um projeto antes, porque é o primeiro a chegar com um protótipo executável. Demonstrável com dados reprodutíveis... 

 

O que escrevo é só uma sugestão, claro. E grátis.

 

Só com o que eu te expliquei já podia ter construído toda a saída do programa e as pesquisas.

 

Mudando as 3 linhas no mesmo programa terá:

 

Tamanho da struct Info: 536

=====>'id: '1024'

           Usuario: ..... 'Usuario  6089'
          mensagem: ..... 'Mensagem 27474'
              data: ..... '14072018'
              pais: ..... 'Pais  9236'
          hashtags: ..... '#teste #hashum #zero'



=====>'id: '1025'

           Usuario: ..... 'Usuario 10050'
          mensagem: ..... 'Mensagem 15542'
              data: ..... '05012019'
              pais: ..... 'Pais  6189'
          hashtags: ..... '#valor #hash #tres'



=====>'id: '1026'

           Usuario: ..... 'Usuario 10633'
          mensagem: ..... 'Mensagem 27998'
              data: ..... '24042019'
              pais: ..... 'Pais 27350'
          hashtags: ..... '#teste #hashum #zero'



=====>'id: '1027'

           Usuario: ..... 'Usuario  3486'
          mensagem: ..... 'Mensagem 27489'
              data: ..... '24122019'
              pais: ..... 'Pais 13115'
          hashtags: ..... '#teste #hashum #zero'

 

Isso não é importante.

 

Eu vi a sugestão no vídeo do Youtube que mostrou. Muito lento e apenas para dar um link de um script que roda no Planilhas do Google e acessa sua conta do twitter e extrai dados para uma planilha no Drive. Tags é o nome. Mas parece que foi escrito com muita atenção. Só que é pouco flexível e não resolve por si só o seu problema. Nem O Google Planilhas porque a exportação é também muito pobre. O programa do Google não faz o simples, como está no  RFC 4180 que te mandei: aparentemente ele só protege alguns campos, e não tem opção para escolher nada. Compare com o comando do mySQL que te mostrei. Muito ingênuo da parte deles não fazer o simples e só repetir  o que se faz há mais de 50 anos. Mas é normal: essas empresas não gostam de exportar dados e o software e grátis afinal...

 

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

@arfneto Segue na imagem do que está aparecendo no momento que compilo, no meu ver acredito que se bem ajustado os comandos fgets ,fread e fseek vai rodar certinho, mas como nunca usei eles não sei o que pode estar errado.. Já fiz uma função busca binária pelo id acredito estar certa por mais simples que está o gerador de id e a própria busca_binaria.

 

Analisando o código compilado acredito que dá pra utilizar aquele arquivo .csv que extrai do docs google por olha ali ele parece querer pegar as partes creio esta mal configurado pois não tenho conhecimento exato dos comandos, o que você pode me dizer? 

image.thumb.png.7184b1cd8bcc4bf8220ca5065115ec3f.png

Segue o código até o momento:

#define  __AMOSTRA__ 20
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <locale.h>

typedef struct
{
    int id;

    char usuario[20];
    char mensagem[280];
    char data[10];
    char pais[20];
    char hashtags[200];

}   Info;

int     pos_id[__AMOSTRA__];
int     id_pos[__AMOSTRA__];

Info*   fabrica(int, FILE *dados);
int     mostra_um(Info*);
int aux = 0;

int main(void)
{
	setlocale(LC_ALL, "Portuguese");
	
	FILE * arquivo = NULL;

    arquivo = fopen("registros_twitter.csv", "rt");

    if (arquivo == NULL)
    {
        printf("Problemas na criacao do arquivo dados.\n");
        return 0;
    }
	
    printf("Tamanho da struct Info: %zd\n",
        sizeof(Info));

    Info* teste = fabrica(20201021, arquivo); // reset

    // gera 3 registros so pra ver

	int i;
	
    for (i = 0; i < __AMOSTRA__; i += 1)
    {
        teste = fabrica(0, arquivo);
        mostra_um(teste);
        // nao precisa mais desse
        free(teste);
    };


	busca_binaria(4, teste);
	
	printf("chegou aqui");
    return 0;
};

int busca_binaria(int search)
{
	int c, primeiro, ultimo, meio;
	
	Info * teste;

	printf("Digite id para busca:\n");
	scanf("%d", &search);

	
	primeiro = 0;
	ultimo = aux - 1;
	meio = (primeiro + ultimo) / 2;
	
	while (primeiro <= ultimo)
	{
		if ( id_pos[meio] < search)
		{
			primeiro = meio + 1;
		}
		else if (id_pos[meio] == search)
		{
			printf("%d encontrado na posição %d\n",search, meio+1);
			return search;
		}
		else
			ultimo = meio - 1;
			meio = (primeiro + ultimo) / 2;
	}
		
	if (primeiro > ultimo)
	{
		printf ("Não existe! %d não está na lista.\n", search);
		getch();
	}
		
	
	
}

Info* fabrica(int reset, FILE *arq)
{

    // prototipos para as hastags
  /*
    const static char* tags[5] =
    {
        { "#teste #hashum #zero"},
        { "#teste"},
        { "#teste #hashum #dois"},
        { "#valor #hash #tres"},
        { "#hashum #zero"}
    };
    */
    int a;
    static char linha[50];
    if (reset != 0)
    {
        srand(reset);
        return NULL;
    };
	     
    Info* novo = (Info*)malloc(sizeof(Info));    // cria um novo Info (registro) toda vez que for chamado no main
    
  

	
	novo->id = aux;
	id_pos[aux] = novo->id; // coloca os ids no vetor id_pos[aux] em ordem
	    
	aux = aux + 1;      // preenche o id 0,1,2,3,4
	
    //fazer teste aqui pra não criar outro id pra um mesmo usuário que enviou mais de uma msg
  
    char *result;
    
    result = fgets(novo->usuario, sizeof(novo), arq);
	fseek(arq,(1 * sizeof(novo->usuario)), SEEK_SET);
	fread(&arq,sizeof(char),2,arq);
	
	printf("%s", novo->usuario);
	




/*    printf(novo->usuario, "Usuario %5d", rand());
    sprintf(novo->mensagem, "Mensagem %5d", rand());
    // data
    sprintf(novo->data, "%02d%02d%04d", 
        1+rand()%28, 1+rand()%12, 2018+rand()%2);
    // pais
    sprintf(novo->pais, "Pais %5d", rand());
    // ecolhe um dos 5 conjuntos de hashtags
    sprintf(novo->hashtags, "%s", tags[rand()%5]);*/
    return novo;
};  // fabrica()

int     mostra_um(Info* um)
{
    printf("\n=====>'id: '%d'\n\n", um->id);
    printf("\t%10s: ..... '%s'\n", "Usuario", um->usuario);
    printf("\t%10s: ..... '%s'\n", "mensagem", um->mensagem);
    printf("\t%10s: ..... '%s'\n", "data", um->data);
    printf("\t%10s: ..... '%s'\n", "pais", um->pais);
    printf("\t%10s: ..... '%s'\n\n\n", "hashtags", um->hashtags);
    return 1;
};

Vou deixar o arquivo registros_twitter.csv como anexo pra você ver..

 

registros_twitter.csv

Link para o comentário
Compartilhar em outros sites

Não entendo o que está tentando fazer. Acho que não entendeu o que tentei te explicar :( 

 

O propósito de ter uma função factory é simular os dados. Se tem um csv confiável não usa uma coisa dessas.

 

Você editou uma função chamada fabrica() para fazer o que? Parar de fabricar. Então porque fabrica()?

 

Não faz sentido. Você não entendeu a ideia. fabrica() fabrica e reproduz dados. Do jeito que for preciso. Em minutos gera qualquer coisa, por exemplo esses posts do twitter, como te mostrei.

 

Seu programa: a origem:
 

Seu programa começa por um csv. E isso vai ser complicado. E não precisa resolver tudo junto. Tampouco precisa de mais que uns poucos registros do csv. No entanto o que tem não está bom, como já te expliquei. Se isso não te foi fornecido, se confia naquele TAGS de que falou para extrair os dados, vai ter grandes problemas. Aquilo não está bem formatado. O arquivo extraído do twitter tem que ser tratado. Até onde eu vi aquele TAGS não faz a conversão correta. E o Google Planilhas também não exporta um csv direito. E o único add-on que eu vi na loja do Google também é fraco.

image.png.6a2b6702be70f75b4815dca4977d2410.png

O tal csv Exporter pouco acrescenta ao que tem no Planilhas.

 

Muita gente sofre com isso pela mesma razão. Se ver na documentação do Google Cloud por exemplo, o componente para bancos de dados gigantes do Google, em 

na documentação do Google Cloud  vai ver que ele usa o mesmo comando que te mostrei antes. E não é nada demais, porque isso está estabelecido desde os anos 70. No entanto o Google Planilhas não faz isso quando voce exporta dados. Nem o Microsoft Excel em versões recentes. E ninguém tem as versões antigas ;) .

 

Porque isso importa?


Como todo mundo que trabalha ou tenta trabalhar com data mining ou big data (no popular: arquivos gigantes de dados) e sem banco de dados, os dados tem que estar perfeitamente formatados. 


No caso do twitter as mensagem podem ter qualquer coisa. Inclusive vírgula e ponto e vírgula. E emojis em Unicode. E aspas, apóstrofes, o d1@b0. 

Uma outra nota: as hashtags, seu último campo, estão no formato JSON, de JavaScript Object Notation, e ia preferir tratar isso em, talvez, em javascript, não em C. Mas não deve ser problema porque são apenas texto.

 

Então para você tratar isso em um programa teria que fazer o que faz naquele comando SQL. Simples. Mas nem o TAGS, nem o Planilhas, nem o Excel, nem o csv Exporter replicam esse óbvio comportamento.:
 

  SELECT [QUERY] INTO OUTFILE ... CHARACTER SET 'utf8mb4'
        FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"'
        ESCAPED BY '\\' LINES TERMINATED BY '\n'

 

Isso está em https://cloud.google.com/sql/docs/mysql/import-export/exporting


O que tinha que acontecer?
 

Abrir um formulário e perguntar qual o delimitador e te dar a opção de envolver os campos no csv com algum caracter, para proteger o separados lá dentro. Como no printf() em C quando você usa "%%c" para escrever "%c" na tela. Sem isso quando esses caracteres aparecem no csv o programa de leitura se perde. Em resumo as opções que estão no comando acima, incluído o encoding...

 

Seu programa: a saída:
 

Ele extrai os dados e gera um arquivo em disco, sequential, simples, um mapeamento direto de cada registro do csv para um registro no arquivo. Se sua struct tem 536 bytes como parece que tem -- sem contar algum possível alinhamento --- então se ler 50 registros do csv vai ter um arquivo de 26800 bytes. 

 

Nesse arquivo estarão os dados que estavam no csv.  Para acessar o registro no disco correspondente à linha 35 do csv você vai usar um fseek(). Qual valor? 34 * sizeof(Info). Eu já te disse isso inúmeras vezes desde o primeiro post em que falei de COBOL (uma linguagem) e mainframes (um tipo de computador) e DAM (um método de acesso).
 

Esse é o protótipo de fseek()
 

int fseek(FILE *stream, long int offset, int whence)

 

Considere por exemplo
 

    long int    deslocamento = 0;
    FILE*       csv = fopen("twitter.csv","w");
    int origem = SEEK_SET; // o inicio, 0

 

E você usaria
 

    int pos = 35;
    deslocamento = (pos - 1) * sizeof(Info);
    printf("para pos = %d deslocamento: %d\n",
        pos,
        deslocamento);

   res = fseek(csv, deslocamento, origem);
   printf("fseek() retornou %d\n");

 

Para colocar você na frente do registro 35. E aí um
 

    Info*    registro;
    res = fread(registro, sizeof(Info), 1, csv);

 

se retornar 1 quer dizer que você acabou d eler o registro 35...

 

Esse é o seu programa: implementar a lógica para você ler no disco a partir do id to twitter e não da posição na sequência. No popular, criar um índice, como em SQL

 

Porque estou escrevendo isso de novo?

 

Acho que o mínimo que sua escola deveria fazer era criar um arquivo csv para cada aluno. Confiável e testado. Todos vão ter o mesmo trabalho e nada tem a ver com o exercício. Você talvez menos porque eu te expliquei com detalhes como prosseguir sem isso.

 

Use fabrica() como eu te mostrei. Continue o seu programa. E até lá a escola terá feito algo a respeito do csv. O arquivo que postou por exemplo não deve passar da terceira linha. 

 

Do modo como está e sem fabrica() o maior trabalho é conseguir extrair os dados do twitter. Ainda mais em C e sem conhecer a API do twitter, que eu imagino que seja seu caso. O enunciado exige mais de 10.000 linhas. Com esse volume a matemática sugere a prática certeza que vai dar ***** na entrada antes de ler os 10.000. 

 

Não entendi porque em seus testes continua insistindo com 4 campos apenas

 

 

 

 

 

 

 

Link para o comentário
Compartilhar em outros sites

@arfneto Opa boa tarde desculpe a demora pra responder, assim falei com a professora ontem e ela me passou que eu tenho de transformar o arquivo .csv em um arquivo binário pra assim separar os dados nas estruturas, assim como mostrei pra ela não sendo em arquivo em binário ela me falou que pula alguns caracteres na hora do uso da função strtok. Mas enfim eu comecei outro programa de testes com um arquivo normal .txt (no lugar do arquivo .csv) com umas palavras no qual eu abro ele e transformo em binário e nisso gera um arquivo de saída .bin, depois abro ele novamente e transformo de volta em .txt pra ver realmente saber se fiz tudo corretamente na hora que transformei em binário. Se eu usar o comando printf após abrir o arquivo binário com o fread ele vem corretamente o que estava escrito no programa, só que na hora que eu gero novamente a saída do arquivo .txt (final2.txt) ele volta a mesma coisa que estava no arquivo binario (final.bin). Entendo que utilizei uma váriavel grande de 200 posiçoes (text[200]) e ela não é totalmente preenchida acredito que seja por isso, mas como eu posso fazer pra realmente na transformação de volta para .txt o arquivo (final2.txt) fique exatamente igual ao arquivo teste.txt?


Segue o código:
 

#define  __AMOSTRA__ 20
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <locale.h>


void converte_bin(char *arquivo)
{
	char text[200];
	int total;

	printf("Convertendo arquivo...\n");



	FILE * arq_csv = fopen(arquivo, "r");


	
    if (arq_csv == NULL)
    {
        printf("Problemas na criacao do arquivo dados.\n");
        exit(0);
    }
	
	while (fgets(text, 200, arq_csv) != NULL)
	{
	printf("%s\n", text);
	}

	
	FILE * arq_bin = fopen("final.bin", "wb");
	   

	fread(text,sizeof(char),200,arq_csv); 

	printf("2%s\n", text);
   	fwrite(text , sizeof(char), 200 , arq_bin );

	if (total != 200)
	{
		printf("Erro ao abrir arquivo\n");
		exit(0);
	}

	
	
	fclose(arq_bin);
	fclose(arq_csv);
}


/*void converte_txt(char *arquivo)
{
	
	printf("Convertendo arquivo...\n");
	char text[200];

	FILE * arq_bin = fopen(arquivo, "rb");
	
	if (!arq_bin)
	{
		printf("Erro ao abrir arquivo\n");
		exit(0);
	}
	
	

	FILE * arq_txt = fopen("final2.txt", "w");

  
  
	fread(text,sizeof(char),200,arq_bin); 
	printf("teste: %s\n",text);  
	fwrite(text , 1 , sizeof(text) , arq_txt );
   
	fclose(arq_bin);
	fclose(arq_txt);
	
}*/
///////////////////////////////////////////////////////////////////

int main(void)
{
	setlocale(LC_ALL, "Portuguese");
	
	converte_bin("teste.txt");

	//converte_txt("final.bin");
	
	printf("chegou aqui");
    return 0;
};

Vou mandar o mesmo arquivo que utilizei pra ser feito o mesmo teste, a função converte_txt está em comentário, primeiro tem que executar com o converte_bin, ai colocar a função converte_bin em comentário e tirar de comentário a converte_txt pra rodar.
 

 

 

teste.txt

Link para o comentário
Compartilhar em outros sites

Você lê de fato o que eu escrevo? Não me perguntou nada sobre o que eu disse. E eu te expliquei como escrever isso.

 

Eu te mostrei a mecânica, as funções e uma possível estrutura de dados. E referências históricas e links sobre o problema.

E te mandei uma função pronta que cria os dados para você resolver TODO o seu programa daí pra frente. E você está tentando escrever outra... Não é boa ideia. Faça o simples. Ou não.

 

13 minutos atrás, Leonardo Ely disse:

eu tenho de transformar o arquivo .csv em um arquivo binário pra assim separar os dados nas estruturas, assim como mostrei pra ela não sendo em arquivo em binário ela me falou que pula alguns caracteres na hora do uso da função strtok

 

Isso é uma ideia ruim. O problema do seu programa não está nele: está no csv de origem. A sua escola deveria extrair um arquivo csv para cada aluno, um arquivo pronto e confiável e seria melhor para todo mundo, porque seria trivial testar os programas dos alunos já que se a escola fornece os dados o arquivo binário gerado está definido totalmente. E as pesquisas. E um programa poderia testar os trabalhos.

 

Mas a escola não fez isso e aparentemente não vai fazer. Essa ideia de usar strtok() não é boa. Já me mandei uma struct com o formato que usa. A que está usando está errada.

 

Já te disse que o csv de origem está errado, e acho que já sabe disso também: é preciso proteger os campos na extração. O Google não faz isso exceto para o CloudSQL e os outros bancos de dados do ecossistema Google Cloud. No Planilhas não vai rolar. O programa que usou também não funciona, o TAGS. 

 

Estou sem tempo agora. Escrevi um programa de teste, usando a função fabrica nos moldes em que te mostrei, just for fun: ele gera o arquivo sem problemas, e leva uns 2s para gerar 50.000 registros no disco. É só um loop afinal. Posso te mandar um arquivo e você testa suas funções a partir dele.

 

Depois posto algo aqui...
 

Você precisa de algo assim
 


teste3("csvout.bin",50000, 3)

teste2("csvout.bin",50000)

teste1("csvout.bin",50000)

Tamanho da struct Info: 532
Gravando 50000 registros em 'csvout.bin'


Gerado 'csvout.bin' com 50000 registros
Tamanho do registro:   532
Tamanho do arquivo:  26600000 [50000 registros + 0 bytes]
Aberto 'csvout.bin'
Registros a mostrar: 3
sizeof(Info) = 532


Registro 49999, offset 26599468 (pos arquivo = 26599468)

=====>'id: '50000'

           Usuario: ..... 'User 3796 id 50000'
          mensagem: ..... 'Mensagem  4541'
              data: ..... '16112019'
              pais: ..... 'Pais 16662'
          hashtags: ..... '#teste #hashum #dois'




Registro 0, offset 0 (pos arquivo = 0)

=====>'id: '1'

           Usuario: ..... 'User 6089 id     1'
          mensagem: ..... 'Mensagem  7474'
              data: ..... '14072018'
              pais: ..... 'Pais  9236'
          hashtags: ..... '#teste #hashum #zero'




Registro 25000, offset 13300000 (pos arquivo = 13300000)

=====>'id: '25001'

           Usuario: ..... 'User 7851 id 25001'
          mensagem: ..... 'Mensagem  1997'
              data: ..... '21052018'
              pais: ..... 'Pais 22313'
          hashtags: ..... '#hashum #zero'

 

O programa simplesmente cria os 50.000 caras, em disco, depois usa fseek() para mostrar o primeiro, o ultimo e um la no meio.

 

void        teste3(const char* arquivo, const unsigned N, const unsigned mostrar[])
{
    printf("\nteste3(\"%s\",%u, %u)\n", arquivo, N, mostrar[0]);
    int res = 0;
    Info    registro;
    teste2(arquivo, N);
    FILE* csv = fopen(arquivo, "r+");
    if (csv == NULL)
    {
        printf("Erro abrindo '%s'\n", arquivo);
        return;
    };
    int cnt = mostrar[0];
    printf("Aberto '%s'\n", arquivo);
    printf("Registros a mostrar: %d\n", cnt);
    printf("sizeof(Info) = %zd\n", sizeof(Info));
    for (int i = 1; i <= cnt; i += 1)
    {
        long offset = mostrar[i] * sizeof(Info);
        res = fseek(csv, offset, SEEK_SET);
        if (res != 0)
        {
            printf("\tErro em fseek()!\n");
            return;
        };
        printf(
            "\n\nRegistro %d, offset %d (pos arquivo = %d)\n",
            mostrar[i], offset, ftell(csv)
        );
        res = fread((void*)&registro, sizeof(Info), 1, csv);
        if (res != 1)
        {
            printf("\tErro de leitura\n");
            return;
        };  // if()
        mostra_um(&registro);
    };  // for()
    fclose(csv);
    return;
};

 

Acima está a função que faz a busca e mostra o resultado que está acima. Veja como a lógica é exatamente a que te mostrei. Faça o simples. Escreva em torno dos dados, simule os dados, termine o programa.

 


typedef struct st_info
{
    int id; 

    char usuario[20]; 
    char mensagem[280];
    char data[8];
    char pais[20];
    char hashtags[200];
     
}   Info;

 

Essa acima é a sua estrutura. Está no enunciado.

 

 

 

Link para o comentário
Compartilhar em outros sites

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...