Dificuldade para gravar dados em um arquivo (Iniciante C) [RESOLVIDO]

1. Dificuldade para gravar dados em um arquivo (Iniciante C) [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 25/02/2019 - 16:57h

Bom dia a todos. Amigos estou apanhando muito pra tentar fazer alguma coisa parecida com o exemplo completo abaixo que mostrarei.

Meu Objetivo é: Ler o Arquivo 1 e gravar somente no Arquivo 2 as linhas sem repeti-las!
Mas não estou conseguindo entender!

Talvez seja algo BOBO mas minha cabeça travou completamente nisso!

#include <stdio.h>
void main (){
FILE *File1 = fopen("1", "w+"), *File2 = fopen("2", "w+");
fprintf(File1, "Linha A\nLinha B\nLinha B\nLinha C\nLinha D\nLinha A\n");

int Ck = 0; char Line_File1[10] = {0}, Line_File2[10] = {0};
rewind(File1);
while(fgets(Line_File1, sizeof(Line_File1), File1) != NULL) {

rewind(File2);

/* O QUE NOTEI é que ele não está lendo linha a linha esse while do 2º Arquivo,
mas não entendo porque o mesmo comando funciona acima para ler o 1º Arquivo! */
while(fgets(Line_File2, sizeof(Line_File2), File2) != NULL)
if(Line_File2 == Line_File1) {
Ck = 1;
break;
}

if(Ck == 0)
fprintf(File2, Line_File1);

Ck = 0;
}
fclose(File1);
fclose(File2);
}

Resultado que eu espero no 2º Arquivo:
Linha A
Linha B
Linha C
Linha D


Se alguém puder ajudar, apontando onde estou errando? OU se existe alguma forma melhor de fazer isso? Lembrando apenas que isso é um exemplo, o arquivo que usarei tem 201 mil linhas, algumas linhas com 17 mil caracteres, e nessas linhas possuem todos os símbolos do teclado!
Acontece que eu consigo reproduzir o arquivo original perfeitamente, tipo fazendo uma cópia, seja linha a linha ou caracter a caracter. O que NUNCA está dando certo é verificar criando a condição se ele vai ou não gravar!

Obrigado!


  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 26/02/2019 - 00:52h

Note porém que seu algoritmo, conquanto funcione, será terrivelmente lento para você filtrar 201.000 linhas de tamanho muito longo (a não ser que você tenha um caso trivial, em que quase todas as linhas sejam iguais), pois cada nova linha terá de ser comparada com todas as anteriores, de modo que o algoritmo tem tempo de execução quadrático (ou seja: proporcional ao quadrado do número de linhas). Assim, se você não quiser acabar com um laço de repetição com dezenas de bilhões de repetições, tem de mudar de estratégia.

Uma forma de reduzir a complexidade do algoritmo é evitando ter de varrer todo o conteúdo do segundo arquivo para cada linha nova lida do primeiro. Usando busca binária, você pode conseguir reduzir a ordem de complexidade de cada busca de n para log n, o que já é um grande ganho. Como serão mais de 200.000 buscas, você terá algo em torno de alguns milhões de comparações, umas mil vezes menos do que com busca linear.

Se você puder usar hashes, a complexidade da busca tende a cair mais ainda, da ordem de n para um valor praticamente constante a cada busca, de modo que a quantidade de operações tende a ser próxima da quantidade de itens.

O problema é que criar arquivos representando árvores e tabelas hash não é trivial de fazer na unha. Felizmente, porém, existem bibliotecas prontas para isso, como Berkeley DB (hoje pertencente à Oracle, mas mesmo a antiga versão 1.85, que costumava vir com os BSD e algumas distribuições de Linux, basta para tanto) e a GDBM.

O exemplo abaixo usa uma tabela hash por meio de Berkeley DB num arquivo temporário, que é apagado ao final do programa. Normalmente o Berkeley DB é usado para associar cada chave de consulta a um valor, mas no caso em questão, interessa apenas a chave de consulta, de modo que o valor associado é deliberadamente deixado vazio.

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <db_185.h> // Ou apenas <db.h>, se for num BSD ou com um Linux antigo.
#include <fcntl.h>
#include <unistd.h>


int main (){
FILE *File1 = fopen("1", "w+"), *File2 = fopen("2", "w+");
fputs("Linha A\nLinha B\nLinha B\nLinha C\nLinha D\nLinha A\n", File1);

rewind(File1);

char Line_File1[10] = {0};

DB *known_lines=dbopen("known_lines.db", O_CREAT|O_TRUNC|O_RDWR, 0644, DB_HASH, NULL);

while(fgets(Line_File1, sizeof(Line_File1), File1) != NULL) {
DBT key, value;
key.data=Line_File1;
key.size=strlen(Line_File1);
if(known_lines->get(known_lines, &key, &value, 0)==1){
value.data=NULL;
value.size=0;
known_lines->put(known_lines, &key, &value, 0);
fputs(Line_File1, File2);
}
}
known_lines->close(known_lines);
unlink("known_lines.db");
fclose(File2);
fclose(File1);
}


Para compilar, use algo como “gcc -Wall -Werror -O2 -pedantic-errors filtra_repetidos.c -o filtra_repetidos -ldb”.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)

3. Re: Dificuldade para gravar dados em um arquivo (Iniciante C)

Paulo
paulo1205

(usa Ubuntu)

Enviado em 26/02/2019 - 00:23h

O erro está na comparação “Line_File2 == Line_File1”. Ao fazê-la, você compara os endereços dos ponteiros (lembrando que eu disse numa mensagem recente que arrays decaem automaticamente para ponteiro em quase todas as expressões, com apenas três exceções), não os conteúdos para os quais eles apontam.

A forma de corrigir é usar a função strcmp() para comparar as strings. Se a função retornar zero, ambas são iguais; se negativo, o primeiro é menor; se positivo, o primeiro é maior.

Outro problema com seu programa é que é inseguro fazer algo como “printf(variavel)” ou “fprintf(arquivo, variavel)/”. Mais prudente seria usar “printf("%s", variavel)” ou “fputs(variavel, arquivo)”. O motivo é simples: do jeito como você fez, variavel será entendida como a string de formatação do fprintf(), o que implica que toda vez que aparecer um caráter '%' seguido de uma possível especificação de conversão, a função vai se comportrar como se você tivesse passado argumentos adicionais à função, os quais conteriam os valores a serem convertidos e impressos (ou salvos em disco) na forma de strings, só que tais parâmetros não existem, o que provocaria uma violação de memória, com consequências imprevisíveis.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


4. Re: Dificuldade para gravar dados em um arquivo (Iniciante C) [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 26/02/2019 - 10:17h

paulo1205 escreveu: O erro está na comparação “Line_File2 == Line_File1”. Ao fazê-la, você compara os endereços dos ponteiros (lembrando que eu disse numa mensagem recente que arrays decaem automaticamente para ponteiro em quase todas as expressões, com apenas três exceções), não os conteúdos para os quais eles apontam. A forma de corrigir é usar a função strcmp() para comparar as strings. Se a função retornar zero, ambas são iguais; se negativo, o primeiro é menor; se positivo, o primeiro é maior.

UAU! Saber isso é muito importante! Eu estava ficando louco tentando entender porque nunca era igual! Agora além de saber o motivo, aprendi a fazer certo!

Outro problema com seu programa é que é inseguro fazer algo como “printf(variavel)” ou “fprintf(arquivo, variavel)/”.

Anotado! Terei que me esforçar em manter isso em mente! De qualquer forma sempre guardo o artigo para reestudar até essas dicas ficarem gravadas!


5. Re: Dificuldade para gravar dados em um arquivo (Iniciante C) [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 26/02/2019 - 10:41h

paulo1205 escreveu: Note porém que seu algoritmo, conquanto funcione, será terrivelmente lento...

Acho que minha mente falhou nessa análise, eu costumo e gosto de pensar em todas as consequências, não entendo como não fiz esses cálculos. Embora essa comparação que esotu fazendo é para meu uso pessoal, pois são de listas de Adblock, ainda assim considero importante para mim fazer CORRETO! Não é apenas uma questão dele funcionar, e sim uma questão de fazer CERTO, porque como disse anteriormente, quero aprende a programar corretamente! Algo como sentir orgulho de fazer um Algoritmo bem feito!

Se você puder usar hashes, a complexidade da busca tende a cair mais ainda

Eu nem sabia que isso existia, mas já fiz uma busca no Google, achei alguns materiais, para estudar sobre isso.

Ao compilar ele me retorna o seguinte erro:
bash-4.4$ gcc -Wall -Werror -O2 -pedantic-errors Test3.c -o Test3 -ldb
Test3.c: In function ‘main’:
Test3.c:33:3: error: implicit declaration of function ‘unlink’; did you mean ‘unix’? [-Wimplicit-function-declaration]
unlink("known_lines.db");
^~~~~~
unix
bash-4.4$

A Solução que encontrei para testar foi remover a linha
unlink("known_lines.db"); 

O Programa funcionou 100% apenas por falta dessa linha ele não apagou esse arquivo, imaginando que seria isso que ela deveria ter feito, eu acho!

Eu Paulo, eu te agradeço muito por ter elaborado esse código, não apenas porque responde a minha pergunta, mas também porque ele me servirá para entender sobre a nova informação (hashes) que vc me passou! Justamente por ele ter poucas linhas e eu saber o que ele faz, isso vai me facilitar muito compreender o funcionamento dele, de forma que um dia poderei fazer um pra mim no futuro!

Obrigado mais uma vez por suas respostas, porque elas não apenas respondem a pergunta feita, mas são uma verdadeira aula mais do que completas, que ajudam a fazer a diferença em nosso mundo! São pequenos gestos como esse que contribuem para a evolução da humanidade! Hoje estou aprendendo, amanhã estarei ajudando um outro, sem essa corrente, não existe evolução!


6. Re: Dificuldade para gravar dados em um arquivo (Iniciante C) [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 26/02/2019 - 11:09h

Nick-us escreveu:

Ao compilar ele me retorna o seguinte erro:
bash-4.4$ gcc -Wall -Werror -O2 -pedantic-errors Test3.c -o Test3 -ldb
Test3.c: In function ‘main’:
Test3.c:33:3: error: implicit declaration of function ‘unlink’; did you mean ‘unix’? [-Wimplicit-function-declaration]
unlink("known_lines.db");
^~~~~~
unix
bash-4.4$


Falha minha ao editar o código online (a remoção do arquivo não estava no protótipo que eu testei em casa): eu esqueci de incluir a linha “#include <unistd.h>”, que é onde unlink() é declarada. Já editei a postagem original para incluí-la.

Note, porém, que aquele código foi só para dar uma ideia, e eu não me preocupei em fazer ajustes nos parâmetros da tabela hash. Tais ajustes podem vir a ser úteis para você, uma vez que você já sabe mais ou menos quantos registros terá e que os tamanhos das chaves de pesquisa podem ser longos. Leia a documentação online de dbopen, hash e btree.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts