C#  -  Deletando arquivos duplicados - II


 Neste artigo vou mostrar como deletar arquivos duplicados em uma pasta usando a linguagem  C#.

Continuando a primeira parte do artigo vamos criar uma aplicação mais genérica para localizar e deletar arquivos duplicados.

Para melhorar um pouco a interface com o usuário vamo usar o controle TreeView para exibir os arquivos duplicados com uma checkbox.

Para consultar os arquivos duplicados vamos verificar o hash code do arquivo, e assim, se dois arquivos possuirem o mesmo hash code temos 99% de certeza de que eles são o mesmo arquivo.

Recursos Usados

Criando o projeto Windows Forms

Abra o VS 2017 Community e crie um novo projeto (File-> New Project) usando a linguagem C# e o template Windows Forms App (.NET Framework).

Informe um nome a seu gosto. Eu vou usar o nome CShp_Arquivos;

No formulário Form1 do projeto inclua os seguintes controles a partir da ToolBox:

  • 1 Label - Text=Diretório
  • 1 TextBox - txtDiretorio
  • 1 Button - btnDiretorio
  • 1 Button - btnProcurar
  • 1 TreeView - tvArquivos
  • 3 Buttons - btnSelecionarDuplicados, DeletarDuplicados e btnSair
  • 1 Label - lblNumeroArquivosDuplicados

Disponha os controles conforme o leiaute da figura abaixo:

Inclua uma referência no projeto ao namespace : Microsoft.VisualBasic

A seguir inclua no formulário os seguintes namespaces:

using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Windows.Forms;
using Microsoft.VisualBasic.FileIO;

No início do formulário vamos declarar as variáveis objeto para criar uma instância do serviço MD5 e definir uma variável para definir o estado de excluir um arquivo:

private MD5 Md5 = MD5.Create();
private bool Deleting = false;

No evento Click do botão - btnDiretorio - para selecionar o diretório inclua o código abra a janela de diálogo FolderBrowserDialog :

No botão para Procurar inclua no evento Click o código abaixo que procurar por arquivo duplicados

      private void btnProcurar_Click(object sender, EventArgs e)
        {
            tvArquivos.Visible = false;
            tvArquivos.Nodes.Clear();
            lblNumeroArquivosDuplicados.Text = "";
            Refresh();
            try
            {
                // Obtem uma lista de arquivos e seus valores de hash
                var get_info =
                    from string _nomeArquivo in Directory.GetFiles(txtDiretorio.Text)
                    select new
                    {
                        Name = _nomeArquivo,
                        Hash = BytesToString(GetHash(_nomeArquivo))
                    };
                // Agrupa os arquivos por valor de hash
                var group_infos =
                    from info in get_info
                    group info by info.Hash into g
                    where g.Count() > 1
                    //orderby g.Key
                    select g;
                // percore os arquivos
                int num_groups = 0;
                int num_arquivos = 0;
                foreach (var g in group_infos)
                {
                    num_groups++;
                    TreeNode hash_node = tvArquivos.Nodes.Add(g.Key.ToString());
                    foreach (var info in g)
                    {
                        num_arquivos++;
                        TreeNode arquivo_node = new TreeNode(info.Name);
                        arquivo_node.Tag = new FileInfo(info.Name);
                        hash_node.Nodes.Add(arquivo_node);
                    }
                }
                // Exibe o numero de arquivos duplicados
                lblNumeroArquivosDuplicados.Text = (num_arquivos - num_groups).ToString() + 
" arquivos duplicados";
                // Expande os nodes.
                tvArquivos.ExpandAll();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                // Cai para o topo
                if (tvArquivos.Nodes.Count > 0)
                    tvArquivos.Nodes[0].EnsureVisible();

                tvArquivos.Visible = true;    
            }
    }

No código usamos uma consulta LINQ que faz um loop nos arquivos retornados pelo método Directory.GetFiles, e retorna os nomes dos arquivos em um diretório.

Para cada arquivo, a consulta seleciona o nome do arquivo e o código hash MD5 do arquivo.

A seguir usamos outra consulta LINQ para agrupar os resultados da primeira consulta por código hash. Selecionando apenas grupos em que o número de arquivos no grupo (isto é, o mesmo código hash) é maior que um. O resultado é uma consulta que retorna grupos contendo pelo menos dois arquivos com os mesmos códigos de hash.

Tendo construído a consulta de agrupamento, o programa percorre os grupos. Para cada grupo, o programa adiciona um nó de nível superior ao controle TreeView exibindo o código de hash. Em seguida, ele passa pelo conteúdo do grupo.

Cada item do grupo contém o nome e o código de hash de um arquivo. O código cria um nó filho para o arquivo, definindo seu texto como igual ao nome do arquivo. Ele também define a propriedade Tag do nó filho como um objeto FileInfo que representa o arquivo. Mais tarde, ele pode usar esse objeto para exibir ou excluir o arquivo.

Conforme processa os grupos, o código mantém o controle do número de arquivos duplicados e do número de grupos. Depois, ele informa o número de arquivos duplicados.

Continuando, o código expande todos os nós do controle TreeView para que você possa ver todos os arquivos duplicados. Ele também rola para o topo do controle TreeView.

A seguir temos os métodos BytesToString() e GetHash() usados para realizar as tarefas de retornar um hash como uma string e calcular o hash do arquivo:

No evento Click do botão Selecionar Arquivos duplicados temos o código que marca os checkbox dos arquivos duplicados:

Finamente temos o evento Click do botão - Deletar Arquivos - que inclui o arquivo na lixeira e depois deleta o arquivo :

 private void DeletarDuplicados_Click(object sender, EventArgs e)
        {
            Deleting = true;
            tvArquivos.Visible = false;
            lblNumeroArquivosDuplicados.Text = "";
            Refresh();
            try
            {
                // Faz uma lista de nodes para deletar
                List<TreeNode> nodes_para_deletar = new List<TreeNode>();
                foreach (TreeNode hash_node in tvArquivos.Nodes)
                {
                    foreach (TreeNode arquivo_node in hash_node.Nodes)
                    {
                        // Verifica se o arquivo do node esta marcado(checked.)
                        if (arquivo_node.Checked) nodes_para_deletar.Add(arquivo_node);
                    }
                }
                // Deleta os nodes selecionados e seus arquivos
                foreach (TreeNode arquivo_node in nodes_para_deletar)
                {
                    // Obtem o FileInfo da propriedadae Tag
                    FileInfo file_info = arquivo_node.Tag as FileInfo;
                    // Move o arquivo para a lixeira
                    DeletarArquivo(file_info.FullName);
                    // Remove o arquivo do TreeView.
                    arquivo_node.Remove();
                }
                // Faz uma lista do tamanho dos nodes com os filhos restantes
                nodes_para_deletar = new List<TreeNode>();
                foreach (TreeNode hash_node in tvArquivos.Nodes)
                {
                    if (hash_node.Nodes.Count == 0) nodes_para_deletar.Add(hash_node);
                }
                // Deleta o tamanho dos nodes com os filhos
                foreach (TreeNode hash_node in nodes_para_deletar)
                {
                    hash_node.Remove();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                // Rola para o topo 
                if (tvArquivos.Nodes.Count > 0)
                    tvArquivos.Nodes[0].EnsureVisible();
                tvArquivos.Visible = true;
                Deleting = false;
            }
        }

A seguir temos o método DeletarArquivo() e suas sobrecargas :

Executando o projeto e iremos obter:

Pegue o projeto completo aqui :    CShp_ArquivosDuplicados.zip

"Disse-lhe Jesus: Eu sou o caminho, e a verdade e a vida; ninguém vem ao Pai, senão por mim."
João 14:6

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

 

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

 

Referências:


José Carlos Macoratti