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校验)的主要内容,如果未能解决你的问题,请参考以下文章
Crimsonland 血腥大地 逆向无敌通关分析报告配置文件加密Dll隐藏MD5检测补丁反调试函数反反调试