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