Qt实现一个重复文件检测小工具(原理:通过md5校验)

Posted 林夕07

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt实现一个重复文件检测小工具(原理:通过md5校验)相关的知识,希望对你有一定的参考价值。

介绍

先看成品图

设计原理

通过递归遍历文件夹获取到所有文件,然后将所有的文件通过线程的形式进行md5(MD5信息摘要算法)计算得到一个32位的十六进制序列,以这个序列为key,对应的文件名为value(此处可能有多个),所以这个map设计为QHash<QByteArray, QStringList>。下面的进度条是在每进行一个md5计算就发出一个信号,然后更新进度条。

整体框架


MainWindows类用于ui显示和一些逻辑代码。
FileMd5类用于计算MD5数值。

信号与槽

connect(ui->GetFiles, QOverload<bool>::of(&QPushButton::clicked),
                  this, QOverload<bool>::of(&MainWindow::onGetFiles));
connect(&md5, QOverload<const QHash<QByteArray, QStringList>&>::of(&FileMd5::GotFilesMd5),
                  this, QOverload<const QHash<QByteArray, QStringList>&>::of(&MainWindow::onGotFilesMd5));
connect(this, QOverload<const QString&>::of(&MainWindow::GotFilesMd5),
                  &md5, QOverload<const QString&>::of(&FileMd5::onGetFileMd5));
connect(&md5, QOverload<int, int>::of(&FileMd5::NowProgress),
                  this, QOverload<int, int>::of(&MainWindow::onNowProgress));
connect(ui->listWidgetMd5, QOverload<const QString &>::of(&QListWidget::currentTextChanged),
                  this, QOverload<const QString &>::of(&MainWindow::onCurrentTextChanged));

填充QHash<QByteArray, QStringList>

void FileMd5::onGetFileMd5(const QString &path)

    QHash<QByteArray, QStringList> ret;
    QStringList files = GetFiles(path);
    for (int i = 0; i < files.size(); ++i)
    
         QByteArray md5 = GetFileMd5(files.at(i)).toHex(); // 计算md5值
         qDebug() << files.at(i) << "\\t" << md5;

         ret[md5].append(files.at(i));

         emit NowProgress(i + 1, files.size()); // 发送当前进度
    
    emit GotFilesMd5(ret); 


计算单个文件的Md5

QByteArray FileMd5::GetFileMd5(const QString &fileName)

    QFile file(fileName, this);
    const bool isOpen = file.open(QIODevice::ReadOnly);
    if( true == isOpen)//以只读形式打开文件
    
        QCryptographicHash hash(QCryptographicHash::Md5);
        while(false == file.atEnd())
        
            QByteArray data = file.read(100 * 1024 * 1024);// 100m  实际内容若不足只读实际大小
            //QByteArray catalog = file.readAll(); // 小文件可以一直全读在内存中,大文件必须分批处理

            hash.addData(data);

            qApp->processEvents();//执行事件循环  防止界面卡顿。
        
        QByteArray md5 = hash.result();
        file.close();//及时关闭
        return md5;
    
    return QByteArray();


递归遍历获得所有所有文件

QStringList FileMd5::GetFiles(const QString &path)

    QStringList ret;

    QDir dir(path);
    QFileInfoList infoList = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);//返回文件信息  不要当前目录和上一级目录

    for (int i = 0; i < infoList.count(); ++i)
    
        QFileInfo info = infoList.at(i);
        if(true == info.isDir()) // 是目录就继续递归
        
            QStringList files = GetFiles(info.absoluteFilePath());
            ret.append(files);
        
        else // 文件就直接给追加文件名
        
            //qDebug() << info.fileName();
            //qDebug() << info.absoluteFilePath();
            ret.append(info.absoluteFilePath());
            //qDebug() << info.absoluteFilePath();
        
    
    return ret;


设置进度条数值

void MainWindow::onNowProgress(int curr, int total)

    // 方法一
    //ui->progressBar->setValue(static_cast<double>(curr) / total * 100);

    // 方法二
    ui->progressBar->setValue(curr);
    ui->progressBar->setMaximum(total);
    ui->progressBar->setMinimum(0);


获取当前Md5值下重复的文件

void MainWindow::onCurrentTextChanged(const QString &text)

    ui->listWidgetRepetition->clear();
    //qDebug() << text;
    QByteArray temp = text.toUtf8();
    qDebug() << temp;
    QStringList files = this->md5Map[text.toLocal8Bit()];

    ui->listWidgetRepetition->addItems(files);


完整代码

FileMd5.h

#ifndef FILEMD5_H
#define FILEMD5_H

#include <QObject>
#include <QStringList>
#include <QHash> // 无序 快
#include <QMap> // 有序 慢

class FileMd5 : public QObject

    Q_OBJECT
public:
    FileMd5(QObject* parent = nullptr);

signals:
    void GotFilesMd5(const QHash<QByteArray, QStringList>& md5);

    // 将进度传出去
    void NowProgress(int curr, int total);

public slots:
    void onGetFileMd5(const QString& path);

private:
    QStringList GetFiles(const QString& path);

    QByteArray GetFileMd5(const QString& fileName);

;

#endif // FILEMD5_H

FileMd5.cpp

#include "FileMd5.h"
#include <QFile>
#include <QMessageBox>
#include <QDebug>
#include <QCryptographicHash>
#include <QApplication>
#include <QDir>  // 目录类
#include <QFileInfo> // 文件信息类
FileMd5::FileMd5(QObject *parent)




void FileMd5::onGetFileMd5(const QString &path)

    QHash<QByteArray, QStringList> ret;
    QStringList files = GetFiles(path);
    for (int i = 0; i < files.size(); ++i)
    
         QByteArray md5 = GetFileMd5(files.at(i)).toHex();
         qDebug() << files.at(i) << "\\t" << md5;

         ret[md5].append(files.at(i));

         emit NowProgress(i + 1, files.size());
    
    emit GotFilesMd5(ret);


QStringList FileMd5::GetFiles(const QString &path)

    QStringList ret;

    QDir dir(path);
    QFileInfoList infoList = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);//返回文件信息  不要当前目录和上一级目录

    for (int i = 0; i < infoList.count(); ++i)
    
        QFileInfo info = infoList.at(i);
        if(true == info.isDir()) // 是目录就继续递归
        
            QStringList files = GetFiles(info.absoluteFilePath());
            ret.append(files);
        
        else // 文件就直接给追加文件名
        
            //qDebug() << info.fileName();
            //qDebug() << info.absoluteFilePath();
            ret.append(info.absoluteFilePath());
            //qDebug() << info.absoluteFilePath();
        
    
    return ret;


QByteArray FileMd5::GetFileMd5(const QString &fileName)

    QFile file(fileName, this);
    const bool isOpen = file.open(QIODevice::ReadOnly);
    if( true == isOpen)//以只读形式打开文件
    
        QCryptographicHash hash(QCryptographicHash::Md5);
        while(false == file.atEnd())
        
            QByteArray data = file.read(100 * 1024 * 1024);// 100m  实际内容若不足只读实际大小
            //QByteArray catalog = file.readAll(); // 小文件可以一直全读在内存中,大文件必须分批处理

            hash.addData(data);

            qApp->processEvents();//执行事件循环  防止界面卡顿。
        
        QByteArray md5 = hash.result();
        file.close();//及时关闭
        return md5;
    
    return QByteArray();


MainWindows.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QThread>
#include "FileMd5.h"

QT_BEGIN_NAMESPACE
namespace Ui  class MainWindow; 
QT_END_NAMESPACE

class MainWindow : public QMainWindow

    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    QStringList GetFiles(const QString& path);

    QByteArray GetFileMd5(const QString& fileName);

signals:
    void GotFilesMd5(const QString& path);
private slots:
    void onGetFiles(bool checked = false);
    void onGotFilesMd5(const QHash<QByteArray, QStringList>& md5);
    void onNowProgress(int curr, int total);
    void onCurrentTextChanged(const QString& text);
private:
    Ui::MainWindow *ui;

    // md5, (file1, file2)  相同的文件放在QstringList中
    FileMd5 md5;
    QThread thread;
    QHash<QByteArray, QStringList> md5Map;
;
#endif // MAINWINDOW_H

MainWindows.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QCryptographicHash>
#include <QDebug>
#include <QFile>
#include <QFileDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)

    ui->setupUi(this);
    this->setWindowTitle("重复文件检测");
    thread.start();
    md5.moveToThread(thread.thread()); // thread() 返回 QThead*

    bool ret = connect(ui->GetFiles, QOverload<bool>::of(&QPushButton::clicked),
                  this, QOverload<bool>::of(&MainWindow::onGetFiles));
    qDebug() << ret;

    // 如果编译出现 Make sure 'QHash<QByteArray, QStringList>' is registered using qRegisterMetatype 就是需要注册这个类型。
    qRegisterMetaType<QHash<QByteArray, QStringList>>("QHash<QByteArray, QStringList>");

    ret = connect(&md5, QOverload<const QHash<QByteArray, QStringList>&>::of(&FileMd5::GotFilesMd5),
                  this, QOverload<const QHash<QByteArray, QStringList>&>::of(&MainWindow::onGotFilesMd5));
    qDebug() << ret;

    ret = connect(this, QOverload<const QString&>::of(&MainWindow::GotFilesMd5),
                  &md5, QOverload<const QString&>::of(&FileMd5::onGetFileMd5));
    qDebug() << ret;
    ret = connect(&md5, QOverload<int, int>::of(&FileMd5::NowProgress),
                  this, QOverload<int, int>::of(&MainWindow::onNowProgress));
    qDebug() << ret;
    ret = connect(ui->listWidgetMd5, QOverload<const QString &>::of(&QListWidget::currentTextChanged),
                  this, QOverload<const QString &>::of(&MainWindow::onCurrentTextChanged));
    qDebug() << ret;

   // ui->OpenFile->hide();//设置隐藏
    ui->lineEdit->setReadOnly(true);//设置只读


MainWindow::~MainWindow()

    thread.exit();
    thread.wait(10000);
    delete ui;


void MainWindow::onGetFiles(bool checked)

    QString path = QFileDialog::getExistingDirectory(this, "选择文件夹", ".", QFileDialog::ShowDirsOnly);//仅显示目录 且默认当前路径
    ui->lineEdit->setText(path);

    ui->progressBar->setValue(0);
    emit GotFilesMd5(path);



void MainWindow::onGotFilesM

以上是关于Qt实现一个重复文件检测小工具(原理:通过md5校验)的主要内容,如果未能解决你的问题,请参考以下文章

md5校验工具是干啥用的?

Crimsonland 血腥大地 逆向无敌通关分析报告配置文件加密Dll隐藏MD5检测补丁反调试函数反反调试

linux小技巧

谁可以告诉我md5加密原理

hashlib.md5()函数来筛选出系统重复文件并移除...

MD5、SHA1、CRC32这三个用于检测文件是不是被修改过,哪个可靠性最高?