Arquivos duplicados [RESOLVIDO]

1. Arquivos duplicados [RESOLVIDO]

doom load
doomload

(usa Ubuntu)

Enviado em 04/10/2016 - 16:41h

Boa tarde pessoal, será que vocês podem me dar uma ajuda por favor?

Eu estou fazendo em C um programa que calcula os hashs dos arquivos que estão na mesma pasta que o programa e compara um a um para ver se existem arquivos iguais mas com nomes diferentes, então eu calculei o hash e coloquei tudo dentro de um arquivo, porém estou tendo dificuldade para fazer a comparação, mais exatamente estou tendo problemas com o trecho de código abaixo:


//comparando hash
int i, j = 0;
while((fscanf(fp, "%s %s", hashArquivo, nomeArquivo)) != EOF)
{
printf("i = %d e hashArquivo = %s\n", i, hashArquivo);
getchar();
while((fscanf(fp, "%s %s", hashArquivoCmp, nomeArquivoCmp)) != EOF)
{
printf("j = %d e hashArquivoCmp = %s\n", i, hashArquivoCmp);
getchar();
if (i != j)
{
if ((strcmp(hashArquivo, hashArquivoCmp)) == 0)
{
printf("%s %s\n", hashArquivo, hashArquivoCmp);
puts("arquivos iguais");
getchar();
}
}
j++;
}
}


Quando eu executo, até a parte em que ele mostra o número de linhas e exibe a mensagem esta tudo certo, porém ao pressionar enter, o programa ao invés de seguir em frente, interrompe a função e retorna ao menu principal, alguém consegue entender onde foi que eu errei?

Obrigado


  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 10/10/2016 - 22:15h

O objetivo da string de formatação foi proteger o programa contra eventuais dados inválidos, que poderiam exceder os tamanhos máximos das strings ou conter valores inapropriados. Como todos os dados com os quais o programa trabalha são strings, eu me valho de duas coisas: a especificação de tamanhos máximos que cada string pode ter, e da limitação de quais caracteres podem estar presentes em cada string.

Na verdade eu errei acima na especificação da conversão da primeira string: eu deveria ter dito "%32[0-9a-f]", para aceitar apenas os 32 dígitos hexadecimais que compõem uma assinatura MD5.

Entre a primeira e a segunda conversões, eu uso um espaço em branco, que instrui a função a ignorar qualquer quantidade de espaços em branco após o fim da assinatura MD5 e o início do nome do arquivo. Talvez essa ideia não tenha sido das melhores, uma vez que impede o eventual reconhecimento do nome de um arquivo começando com espaços.

A segunda conversão limita o tamanho a 4095 caracteres, e restringe os possíveis caracteres que podem compor a string aos que forem diferentes de '\n'.

No final, eu uso o artifício de uma conversão com tamanho máximo de 1 caráter, que necessariamente tem de ser igual a '\n', suprimindo, no entanto, a atribuição da string convertida para qualquer array de caracteres. Com isso, o programa retira a marca de fim de linha do buffer de leitura, deixando o ponteiro de leitura já no início da próxima linha na próxima iteração do laço de repetição.

A documentação da família de funções de scanf() do Linux é muito boa. Recomendo sua leitura.

3. Re: Arquivos duplicados [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 05/10/2016 - 15:22h

Estou tentando entender o código, mas algumas coisas me escapam.

Como o valor de i nunca muda, para que você o está usando?

Qual o sentido de comparar i com j?

O loop mais interno usa o mesmo ponteiro de arquivo que o mais externo. Como a única condição de parada do loop mais interno é chegar ao fim do arquivo, e essa é também a condição de parada do loop externo, o loop externo só vai ter uma iteração. É isso mesmo que você quer?


4. Re: Arquivos duplicados [RESOLVIDO]

doom load
doomload

(usa Ubuntu)

Enviado em 05/10/2016 - 16:51h

Olá boa tarde, realmente faltou incrementar o valor de i.

No trecho que eu ocultei eu gero um arquivo de hash então eu utilizo md5sum * > hashArquivo.txt

A estrutura que ele gera é a seguinte:

hashDoArquivo nomeDoArquivo

Eu quero descobrir arquivos duplicados com nomes diferentes, então eu inicio na primeira linha e percorro até o fim do arquivo comparando com os demais hashs, depois eu pulo para a segunda linha e assim por diante, por isso eu comparo i com j, pois eu não vou verificar o hash do arquivo com ele mesmo.

Mesmo eu tendo um ou outro erro, eu fiz as linhas utilizando o par printf e getchar para acompanhar essa iteração linha a linha porém ao invés de exibir a mensagem e parar para eu pressionar um tecla para continuar, o programa não faz nada e retorna ao menu principal, e isso eu não estou conseguindo entender.

Obrigado



5. Re: Arquivos duplicados

Paulo
paulo1205

(usa Ubuntu)

Enviado em 05/10/2016 - 17:10h

doomload escreveu:

Olá boa tarde, realmente faltou incrementar o valor de i.

No trecho que eu ocultei eu gero um arquivo de hash então eu utilizo md5sum * > hashArquivo.txt


Seu programa pode ficar mais simples se você usar “md5sum * | sort > hashArquivo.txt”, pois então o seu algoritmo de comparação poderá ficar linear, em vez de quadrático.

Eu quero descobrir arquivos duplicados com nomes diferentes, então eu inicio na primeira linha e percorro até o fim do arquivo comparando com os demais hashs, depois eu pulo para a segunda linha e assim por diante, por isso eu comparo i com j, pois eu não vou verificar o hash do arquivo com ele mesmo.


Se você ordenar por hash, só terá de comparar uma linha com a linha seguinte. Quando chegar ao fim do arquivo, acabou.

Se você quiser fazer de forma quadrática (comparar linha 1 com linhas 2 a N, depois a 2 com as linhas 3 a N, depois a 3 com a linhas 4 a N, ..., até a linha N-1 com a linha N), pode fazer. Mas, além de mais demorado, você terá de colocar pelo menos uma função que saiba voltar o ponteiro do arquivo para a linha da vez da iteração do loop mais externo.

Mesmo eu tendo um ou outro erro, eu fiz as linhas utilizando o par printf e getchar para acompanhar essa iteração linha a linha porém ao invés de exibir a mensagem e parar para eu pressionar um tecla para continuar, o programa não faz nada e retorna ao menu principal, e isso eu não estou conseguindo entender.


Fica difícil saber causas para eventual erro de leitura do terminal sem ver o programa todo e sem saber como você o está executando. Do jeito como está, eu ACHO que pode dar problema, por exemplo, se você tentar rodá-lo com “nome_do_programa | more”, porque tanto o programa quanto o more podem acabar competindo pelos dados que venham do terminal.


6. Re: Arquivos duplicados [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 05/10/2016 - 17:24h

Ah, outra coisa: se o nome de algum dos seus arquivos contiver espaços, seu programa vai quebrar, já que a conversão "%s" da família scanf() considera que a string não contém espaços. Recomendo que você mude o modo de ler as linhas do arquivo.

(Aliás, para ser purista ao extremo, a própria noção de linha pode vir a ser um problema, uma vez que nomes de arquivo no UNIX podem conter quebras de linhas.)


7. Re: Arquivos duplicados [RESOLVIDO]

doom load
doomload

(usa Ubuntu)

Enviado em 06/10/2016 - 16:01h

Pois é Paulo, eu já tinha notado o problema do espaço no nome dos arquivos, até ai tudo bem, mas eu agora descobri que estava fazendo um uso equivocado da função fscanf, pelo menos para o meu nível de conhecimento, vou procurar estudar ela mais a fundo, pois eu tinha declarado um FILE *fp e estava tentando usar o mesmo ponteiro para comparar duas variáveis diferentes, agora eu fiz a seguinte modificação, eu declarei dois ponteiros do tipo FILE e percorro eles usando um while externo e outro interno, como eu já vinha fazendo, a diferença é que eu abro o ponteiro interno com fopen antes do while interno e fecho no fim do while externo, para sempre retornar ao inicio do arquivo, já que eu desconheço uma outra maneira de retornar ao inicio do arquivo, para o uso atual basta, mas como eu disse, eu preciso procurar informações mais a fundo. Para não ficar confuso o que eu disse acima segue o trecho do código que eu usei para teste agora:


FILE *fp, *fp2;
char s[20] = "hashArquivos.txt";
fp = fopen(s, "r");
if (fp == NULL)
{
puts("Impossivel abrir o arquivo");
getchar();
}
else
{
int i, j = 0;
while((fscanf(fp, "%s %s", hashArquivo, nomeArquivo)) != EOF)
{
printf("%s %s - %d\n", hashArquivo, nomeArquivo, i);
i++;
j = 0;
fp2 = fopen(s, "r");
while((fscanf(fp2, "%s %s - %d\n", hashArquivoCmp, nomeArquivoCmp)) != EOF)
{
printf("%s %s - %d\n", hashArquivoCmp, nomeArquivoCmp, j);
j++;
}

fclose(fp2);
}
puts("fechando arquivos...");
getchar();
fclose(fp);

}


Quanto ao sort seria interessante para identificar manualmente os arquivos duplicados, mas o meu objetivo é excluir os duplicados automaticamente e podem haver mais de 2 iguais na mesma pasta, muito obrigado até mais.


8. Re: Arquivos duplicados [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 06/10/2016 - 16:44h

Eu não estou falando de fazer manualmente. O sort facilita a sua vida como programador também pois, como eu disse antes, você não vai mais precisar de dois loops, pois vai bastar comparar o md5 de uma linha com o da linha seguinte.

Tenha em mente que sua solução de ter dois ponteiros para o mesmo arquivo é uma coisa que funciona no mundo UNIX mas que pode não funcionar em outros sistemas (não sei se é o caso do Windows mas, na época do MS-DOS, era uma coisa que não funcionava muito bem).

EM TEMPO: A string de formatação no fscanf() do loop interno parece estar errada. Verifique-a.


9. Re: Arquivos duplicados [RESOLVIDO]

doom load
doomload

(usa Ubuntu)

Enviado em 06/10/2016 - 17:10h

Opa corrigido, fiz uma pequena confusão rs, mas no teste tinha funcionado bem, eu confesso que eu não entendo como iterar comparando uma linha com a outra, eu entendo como percorrer do inicio ao fim do arquivo, agora iterar linha a linha isso me causa uma confusão ainda e eu não consigo enxergar de que forma posso fazer isso.

Obrigado.


10. Re: Arquivos duplicados [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 07/10/2016 - 09:00h

Abra o arquivo.
Leia o primeiro registro.
Se der erro de leitura, encerre.
Copie o registro lido para a “anterior”.
Enquanto conseguir ler novo registro,
    se o registro lido for igual a “anterior”,
        indique que o registro é igual;
    copie o registro lido por cima de “anterior”.
Feche o arquivo.


Mas, como eu disse antes, isso só vai funcionar se o arquivo estiver ordenado.

Com pequenas adaptações você conseguirá contar o número de repetições, ou criar uma lista de nomes dos arquivos repetidos.


11. Re: Arquivos duplicados [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 07/10/2016 - 11:08h

O código que você postou não tem a comparação para ver se os hashes são iguais. Creio que você esqueceu dela na hora de transcrever.

De todo modo, para cada um dos N registros, você abre o arquivo de novo e lê os N registros num segundo loop. Fazendo as contas, isso significa que você vai ler, e supostamente fazer comparações, de N×N (ou N²) registros. Então, se você tiver cem arquivos, terá dez mil operações de leitura e comparações. Se forem mil arquivos, serão um milhão de leituras e comparações.

Note também que, desse jeito, você compara o mesmo par de registros duas vezes. Por exemplo, se o segundo e o décimo registro forem iguais, isso vai aparecer quando i=2 e j=10 e quando i=10 e j=2.

Você poderia melhorar ligeiramente seu algoritmo, evitando a comparação duplicada e reduzindo seu tempo de execução aproximadamente pela metade (em vez de N² operações, seriam (N²+N)/2). Outro benefício é que você trabalharia com apenas um ponteiro de arquivo, ao custo de introduzir uma operação de deslocamento para trás.

Normalmente eu não coloco código com resposta aqui no fórum. Mas o objetivo agora é ser didático: você tem um código funcionando, mas eu quero, na postagem anterior e nesta, mostrar opções melhores.

char hash1[33], hash2[33];
char file1[4096], file2[4096];
int line1, line2;
long saved_position;
int result;

hash1[32]=hash2[32]=file1[4095]=file2[4095]='\0'; // Garante que todas as strings terão o byte terminador.
line1=0;
rewind(fp); // Volta para o início do arquivo para ter certeza de que vai ler todos os registros.
while((result=fscanf(fp, "%32[0-9a-z] %4095[^\n]%*1[\n]", hash1, file1))==2){
line1++;
line2=line1;
saved_position=ftell(fp); // Guarda posição atual do ponteiro de arquivo.
while((result=fscanf(fp, "%32[0-9a-z] %4095[^\n]%*1[\n]", hash2, file2))==2){
line2++;
if(strcmp(hash1, hash2)==0){
printf("Arquivos \"%s\" e \"%s\" têm a mesma assinatura.\n", file1, file2);
}
}
if(result==EOF)
fseek(fp, saved_position, SEEK_SET);
else
break;
}
if(result!=EOF){
fprintf(stderr, "Formato inválido ao tentar ler a linha %d.\n", line2+1);
}



12. Re: Arquivos duplicados [RESOLVIDO]

doom load
doomload

(usa Ubuntu)

Enviado em 10/10/2016 - 16:38h

Boa tarde Paulo,

Eu não tinha comentado, eu apenas tinha postado a funcionalidade do laço, por isso não tinha colocado a comparação de strings.

Quanto ao problema do i e j como você disse "i=2 e j=10 e quando i=10 e j=2" eu tinha feito uma condição if (i < j).

Este tópico tem sido muito esclarecedor para mim e eu tenho aprendido muito além do que eu imaginava, mas fiquei com uma dúvida quanto ao código que você fez e gostaria de entender, se você puder responder por favor:

1) Eu achei interessante que você fez "%32[0-9a-z] %4095[^\n]%*1[\n]" e eu inclusive não sabia que eu poderia utilizar esse tipo de notação, a primeira string eu compreendi perfeitamente o que você fez mas na segunda "%4095[^\n]%*1[\n]" eu fiquei com dúvida, você captura até 4095 posições e o resto eu não compreendi e não consegui achar nada falando a respeito, o primeiro [^\n] eu não entendi o que faz, nem o %*1[\n], parece que você faz uma supressão, se não me engano é esse o nome, mas eu não consegui entender como aplicar e qual é o sentido exato, será que você pode explicar por favor?

Eu acabei conhecendo a função rewind e para que serve, também vi como é uma estrutura de um ponteiro tipo FILE e graças ao seu código eu vi que a função fscanf retorna o número de parâmetros obtidos com sucesso.

Obrigado



01 02



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts