2016-06-01 3 views
0

Мне нужно написать небольшую консольную программу для интервью с интернетом разработчика, и что-то большое и очень трудное для поиска происходит неправильно. Я должен написать программу, которая проверяет каталог, полный двоичных файлов .dat для дубликатов файлов.Странное поведение в консольной программе

Что я сделал: Я ввожу файл, используя stdin из main.cpp, и если каталог существует, я передаю путь к моей функции fileChecker, которая затем генерирует хеши MD5 для всех файлов в данной директории, а затем создает QHash с именами файлов в качестве ключа и хешей в качестве значений. Затем я пытаюсь перебрать QHash с помощью итератора в стиле Java. Когда я запускаю программу, она полностью сработает, и мне нужно выбрать программу отладки или завершения, которая не позволяет мне понять, что происходит не так, поскольку отладчик QT ничего не выводит.

Я предполагаю, что что-то не так с моей функцией getDuplicates в файле fileChecker.cpp, поскольку я никогда не использовал итераторы java-стиля, прежде чем перебирать QHash. Я пытаюсь взять первую пару «ключ-значение» и сохранить ее в двух переменных. Затем я удаляю эти значения из QHash и пытаюсь повторить оставшуюся часть 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

Вы должны вернуть значение в двух методах 'generateChecksum' и' getDuplicateList'. – GAVD

+0

@ GAVD оба эти метода возвращают значения? – user2094257

+0

Просто комментарий для улучшения, например, 'getDuplicateList' должен возвращать тип' QStringList'. – GAVD

ответ

1

Помимо вашей печальной проблемы управления Qt памяти, вы действительно не должны вычислить md5 суммы всех файлов.

Только для групп файлов одинакового размера :)

Файлы с уникальным размером могут быть опущены. Я бы даже не назвал это оптимизацией, но просто не делал потенциально абсурдного количества ненужной дополнительной работы :)

+0

Где моя проблема управления памятью? Также я думал, что хеши MD5 были рассчитаны на содержимое файла, а не на размер? Не могли ли два или более файла иметь одинаковый размер, но различное содержимое и, следовательно, разные хеши MD5? – user2094257

+1

@ user2094257 Если два файла имеют разные размеры, содержимое отличается, и вам не нужно сравнивать любые хэши, чтобы это сказать. –

+0

@ user2094257 Вы пытались запустить свою программу в режиме отладки? Как только он падает с нарушением сегментации, либо напрямую, потому что какой-то указатель был пустым или косвенным, потому что какой-то указатель свисал, и оттуда произошло ЧУВСТВО, вы, по крайней мере, увидите фрейм стека, который привел к ситуации, и вы можете проверить переменные на стек. – yeoman

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); 
+0

Спасибо за все ваши усилия. Я отредактировал мой код, чтобы включить i.next(), а также j.next(), но я все равно получаю такой же крах. – user2094257

+0

Мой выход сбоя: 2728) CheckProcessInRegistry: список включил, но совпадения не найдено. 2728) IsProcessAllowed (C: \ Qt \ Qt5.3.0 \ Tools \ QtCreator \ Bin \ встроенный duplicateFinder-Desktop_Qt_5_3_0_MinGW_32bit-Debug \ Debug \ duplicateFinder.exe) (80004005) 2728) DllMain (ProcDetach) [OK] 2728) DSound_Unhook 2728) MCIWave_Unhook 2728) AudioClient_Unhook 2728) CAudioStreamMgr :: Shutdown C: \ Qt \ Qt5.3.0 \ Tools \ QtCreator \ Bin \ встроенный duplicateFinder-Desktop_Qt_5_3_0_MinGW_32bit-Debug \ Debug \ duplicateFinder.exe завершался с кодом -1073741819 – user2094257

1

итераторов Всех Qt Java-стиль пришли в «обычных» (сопзИ) и изменяемые версии (где это безопасно для изменения объекта вы итерация). См. QMutableHashIterator. Вы изменяете итератор const; таким образом, он падает.

Пока вы находитесь на нем, посмотрите на функцию findNext, которую предоставляет итератор. Использование этой функции устраняет необходимость в вашем втором итераторе.

+0

Спасибо @Jon Harper Это будет следующей проблемой, я уверен. Я изменил функцию getDuplicates, чтобы возвратить QStringList из двух чисел и удалил весь предыдущий код, который он содержал, чтобы увидеть, будет ли программа работать успешно, однако, когда я вхожу в каталог, который все еще падает, когда я нажимаю клавишу ввода ... Любая идея, как я могу поделитесь тем, что вызывает эту проблему? благодаря – user2094257

0

Некоторые вещи, которые выделяются:

  1. Это плохая идея readAll файла: он будет выделять блок файл размером в куче, только для вычисления его хэш и отказаться от него. Это очень расточительно. Вместо этого используйте QCryptographicHash::addData(QIODevice*): он будет передавать данные из файла, сохраняя только небольшой фрагмент в памяти в любой момент времени.

  2. Вы явно сохраняете дополнительную копию списка записей в папке. Это, вероятно, не нужно. Внутренне, QDirIterator будет использовать специфичные для платформы способы итерации каталога без получения копии списка записей. Только ОС имеет полный список, итератор выполняет только итерацию. Конечно, вы все равно должны держать карту размера, путь-> хэш.

  3. Вы используете итераторы Java. Это довольно многословие. Итераторы стандартного стиля C++ поддерживаются многими контейнерами, поэтому вы можете легко заменить другие контейнеры, например. C++ или повысить производительность/использование памяти.

  4. Вы недостаточно проверите проверку ошибок.

  5. Код кажется слишком многословным для небольшого количества, которое он фактически выполняет. Инкапсуляция всего в класс, вероятно, также является привычкой Java и, скорее, не нужна здесь.

Давайте посмотрим, что может быть самым точным, разумно эффективным способом сделать это. Я пропускаю пользовательские интерфейсы: вы можете либо вызвать его без аргументов для проверки в текущем каталоге, либо с аргументами, первый из которых будет использоваться в качестве пути для регистрации.

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; 
} 
Смежные вопросы