我必须为开发人员实习面试编写一个小型控制台程序,而且很难找到出错的东西。我应该写一个程序,检查一个目录是否包含二进制.dat文件的重复文件。
我做了什么: 我使用main.cpp中的stdin输入一个文件,如果目录存在,我将路径传递给我的fileChecker函数,然后为给定目录中的所有文件生成MD5哈希值,然后创建一个QHash,文件名为key,哈希作为价值观。然后我尝试使用java风格的迭代器迭代QHash。当我运行该程序时,它完全崩溃,我必须选择调试或结束程序,这使我无法弄清楚出现了什么问题,因为QT的调试器没有输出任何东西。
我的猜测是我在fileChecker.cpp中的getDuplicates函数出了问题,因为我之前从未使用过java风格的itterator来尝试使用QHash。我试图获取第一个键值对并将其存储在两个变量中。然后我从QHash中删除这些值,并尝试使用前一个itterator中的itterator迭代QHash的其余部分。如果有人知道我做错了什么,请尽快让我知道,因为我需要在星期一之前完成这个面试... fileChecker.h和fileChecker.cpp的代码如下所示请告诉我如果有更多我可以添加。 感谢
我的代码:
main.cpp:
#include "filechecker.h"
#include <QDir>
#include <QTextStream>
#include <QString>
#include <QStringList>
QTextStream in(stdin);
QTextStream out(stdout);
int main() {
QDir* dir;
FileChecker checker;
QString dirPath;
QStringList duplicateList;
out << "Please enter directory path NOTE: use / as directory separator regardless of operating system" << endl;
dirPath = in.readLine();
dir->setPath(dirPath);
if(dir->exists()) {
checker.processDirectory(dir);
duplicateList = checker.getDuplicateList();
}
else if(!(dir->exists()))
out << "Directory does not exist" << endl;
foreach(QString str, duplicateList){
out << str << endl;
}
return 0;
}
fileChecker.h:
#ifndef FILECHECKER_H
#define FILECHECKER_H
#include <QString>
#include <QByteArray>
#include <QHash>
#include <QCryptographicHash>
#include <QStringList>
#include <QDir>
class FileChecker
{
public:
FileChecker();
void processDirectory(QDir* dir);
QByteArray generateChecksum(QFile* file);
QStringList getDuplicateList();
private:
QByteArray generateChecksum(QString fileName);
QHash<QString, QByteArray> m_hash;
};
#endif // FILECHECKER_H
fileChecker.cpp:
#include "filechecker.h"
FileChecker::FileChecker() {
}
void FileChecker::processDirectory(QDir* dir) {
dir->setFilter(QDir::Files);
QStringList fileList = dir->entryList();
for (int i = 0; i < fileList.size(); i++) {
bool possibleDuplicatesFound = false;
QString testName = fileList.at((i));
QFile* testFile;
testFile->setFileName(testName);
foreach(QString s, fileList) {
QFile* possibleDuplicate;
possibleDuplicate->setFileName(s);
if(testFile->size() == possibleDuplicate->size() && testFile->fileName() != possibleDuplicate->fileName()) {
QByteArray md5HashPd = generateChecksum(possibleDuplicate);
m_hash.insert(possibleDuplicate->fileName(), md5HashPd);
possibleDuplicatesFound = true;
fileList.replaceInStrings(possibleDuplicate->fileName(), "");
}
QByteArray md5Hasht = generateChecksum(testFile);
fileList.replaceInStrings(testFile->fileName(), "");
possibleDuplicatesFound = false;
}
}
}
QByteArray FileChecker::generateChecksum(QFile* file) {
if(file->open(QIODevice::ReadOnly)) {
QCryptographicHash cHash(QCryptographicHash::Md5);
cHash.addData(file->readAll());
QByteArray checksum = cHash.result();
return checksum;
}
}
QStringList FileChecker::getDuplicateList() {
QStringList tempList;
QString tempStr;
QString currentKey;
QByteArray currentValue;
QMutableHashIterator<QString, QByteArray> i(m_hash);
do {
while (i.hasNext()){
i.next();
currentKey = i.key();
currentValue = i.value();
tempStr.append("%1 ").arg(currentKey);
if (i.value() == currentValue) {
tempStr.append("and %1").arg(i.key());
i.remove();
}
tempList.append(tempStr);
tempStr.clear();
}
} while (m_hash.size() > 0);
return tempList;
}
答案 0 :(得分:1)
除了你悲伤的Qt内存管理问题,你真的不必计算所有文件的md5总和。
仅适用于大小相同的文件组:)
可以省略具有唯一大小的文件。我甚至不会称之为优化,而只是不做一些可能荒谬的额外工作:)
答案 1 :(得分:1)
所有Qt Java风格的迭代器都有“常规”(const)和可变版本(可以安全地修改你正在迭代的对象)。见QMutableHashIterator
。你正在修改const
迭代器;因此,它崩溃了。
当你看到它时,请查看迭代器提供的findNext
函数。使用此函数消除了对第二个迭代器的需要。
答案 2 :(得分:0)
只需添加i.next()
,如下所示。
do {
while (i.hasNext()) {
i.next();
currentKey = i.key();
currentValue = i.value();
tempStr.append(currentKey);
m_hash.remove(currentKey);
QHashIterator<QString, QByteArray> j(m_hash);
while (j.hasNext()) {
if (j.value() == currentValue) {
tempStr.append(" and %1").arg(j.key());
m_hash.remove(j.key());
}
}
tempList.append(tempStr);
tempStr.clear();
}
} while (m_hash.size() > 1);
答案 3 :(得分:0)
有些突出的事情:
readAll
文件是个坏主意:它会在堆上分配一个文件大小的块,只计算其哈希并丢弃它。那太浪费了。相反,利用QCryptographicHash::addData(QIODevice*)
:它将从文件中流式传输数据,只在任何给定时间在内存中保留一小块。
您明确保留了文件夹条目列表的额外副本。这可能是不必要的。在内部,QDirIterator
将使用特定于平台的方式迭代目录,而无需获取条目列表的副本。只有操作系统有完整列表,迭代器只迭代它。你仍然需要保持size,path-&gt;哈希映射。
您正在使用Java迭代器。这些都很冗长。许多容器都支持C ++标准样式的迭代器,因此您可以轻松地替换其他容器。 C ++标准库或提升调整性能/内存使用。
您没有做足够的错误检查。
代码似乎过于冗长,因为它实际上做的很少。将所有内容封装到类中可能也是一种Java习惯,而且在这里是不必要的。
让我们看看可能是最合适,最合理的方式。我正在跳过UI细节:你可以不用参数调用它来检查当前目录,或者使用参数,第一个将用作检入的路径。
auto & dupe = entries[size][hash.result()];
是一个强有力的表达。它将构建外部和内部映射中可能缺少的条目。
// https://github.com/KubaO/stackoverflown/tree/master/questions/dupechecker-37557870
#include <QtCore>
#include <cstdio>
QTextStream out(stdout);
QTextStream err(stderr);
int check(const QString & path) {
int unique = 0;
// size hash path
QMap<qint64, QMap<QByteArray, QString>> entries;
QDirIterator it(path, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
QCryptographicHash hash{QCryptographicHash::Sha256};
while (it.hasNext()) {
it.next();
auto const info = it.fileInfo();
if (info.isDir()) continue;
auto const path = info.absoluteFilePath();
auto const size = info.size();
if (size == 0) continue; // all zero-sized files are "duplicates" but let's ignore them
QFile file(path); // RAII class, no need to explicitly close
if (!file.open(QIODevice::ReadOnly)) {
err << "Can't open " << path << endl;
continue;
}
hash.reset();
hash.addData(&file);
if (file.error() != QFile::NoError) {
err << "Error reading " << path << endl;
continue;
}
auto & dupe = entries[size][hash.result()];
if (! dupe.isNull()) {
// duplicate
out << path << " is a duplicate of " << dupe << endl;
} else {
dupe = path;
++ unique;
}
}
return unique;
}
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
QDir dir;
if (argc == 2)
dir = app.arguments().at(1);
auto unique = check(dir.absolutePath());
out << "Finished. There were " << unique << " unique files." << endl;
}