控制台程序中的奇怪行为

时间:2016-06-01 00:59:36

标签: c++ qt

我必须为开发人员实习面试编写一个小型控制台程序,而且很难找到出错的东西。我应该写一个程序,检查一个目录是否包含二进制.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;
}

4 个答案:

答案 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)

有些突出的事情:

  1. readAll文件是个坏主意:它会在堆上分配一个文件大小的块,只计算其哈希并丢弃它。那太浪费了。相反,利用QCryptographicHash::addData(QIODevice*):它将从文件中流式传输数据,只在任何给定时间在内存中保留一小块。

  2. 您明确保留了文件夹条目列表的额外副本。这可能是不必要的。在内部,QDirIterator将使用特定于平台的方式迭代目录,而无需获取条目列表的副本。只有操作系统有完整列表,迭代器只迭代它。你仍然需要保持size,path-&gt;哈希映射。

  3. 您正在使用Java迭代器。这些都很冗长。许多容器都支持C ++标准样式的迭代器,因此您可以轻松地替换其他容器。 C ++标准库或提升调整性能/内存使用。

  4. 您没有做足够的错误检查。

  5. 代码似乎过于冗长,因为它实际上做的很少。将所有内容封装到类中可能也是一种Java习惯,而且在这里是不必要的。

  6. 让我们看看可能是最合适,最合理的方式。我正在跳过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;
    }