/////////////////////////////////////////////////////////////////////// // FindDuplicates.cpp - Find duplicate files // // store each path and each filename only once // // // // Jim Fawcett, CSE687 - Object Oriented Design, Spring 2014 // /////////////////////////////////////////////////////////////////////// /* * Things to make this code more useful: * - find using a collection of patterns like *.h, *.cpp * - use collection of path specifications with wild cards * - Error handling for inaccessible directories * - checking to see if specified path exists * - sort file names independent of case */ #include "FileSystem.h" #include #include #include #include #include using namespace FileSystem; class Duplicates { public: using FilePath = std::string; using FilePaths = std::set; using PathCollection = std::list; using FileName = std::string; using Files = std::vector; using DuplicateFiles = std::map; DuplicateFiles findDuplicates(const FilePath& start); FilePaths& filePaths() { return paths; } private: DuplicateFiles duplicates; DuplicateFiles::iterator hintDuplicatesIter; Files files; FilePaths paths; FilePaths::iterator hintPathsIter = begin(paths); DuplicateFiles find(const FilePath& start); // find all files }; //----< concatenate two File vectors >--------------------------------- Duplicates::Files operator+=(Duplicates::Files& f1, Duplicates::Files& f2) { for (auto file : f2) f1.push_back(file); return f1; } //----< find all files on pathSpec >----------------------------------- Duplicates::DuplicateFiles Duplicates::find(const FilePath& pathSpec) { FilePath path = FileSystem::Path::getFullFileSpec(pathSpec); std::vector localFiles = Directory::getFiles(path, "*.*"); files += localFiles; // add path to paths FilePaths::iterator iter = paths.insert(hintPathsIter, path); hintPathsIter = iter; // add file and iterator to its path to duplicates for (auto file : localFiles) { if (duplicates.find(file) != end(duplicates)) { duplicates[file].push_back(iter); } else { PathCollection paths; paths.push_back(iter); duplicates[file] = paths; } } std::vector dirs = Directory::getDirectories(path, "*.*"); for (auto dir : dirs) { if (dir != "." && dir != "..") { dir = path + "/" + dir; find(FileSystem::Path::getFullFileSpec(dir)); } } return duplicates; } //----< find duplicate files on startSpec >---------------------------- Duplicates::DuplicateFiles Duplicates::findDuplicates(const FilePath& startSpec) { FilePath start = FileSystem::Path::getFullFileSpec(startSpec); find(start); // find non-duplicates Files files; for (auto fileSpec : duplicates) { if (fileSpec.second.size() < 2) files.push_back(fileSpec.first); } // remove non-duplicates for (auto file : files) duplicates.erase(file); return duplicates; } //----< Demonstration >------------------------------------------------ void displayTitle(const std::string& title) { std::string underline = std::string(title.size() + 2, '='); std::cout << "\n " << title; std::cout << "\n " << underline << "\n"; } bool testArgs(int argc, char* argv[]) { if (argc < 2) { std::cout << "\n Enter starting path on command line"; return false; } if (!FileSystem::Directory::exists(argv[1])) { std::cout << "\n specified starting path does not exist" << "\n\n"; return false; } return true; } int main(int argc, char* argv[]) { if (!testArgs(argc, argv)) return 1; std::string pathSpec = FileSystem::Path::getFullFileSpec(argv[1]); std::string title = "Finding Duplicate Files on Path \"" + pathSpec + "\""; displayTitle(title); std::string startPath = argv[1]; Duplicates dups; Duplicates::DuplicateFiles dupFiles = dups.findDuplicates(startPath); if (dupFiles.size() == 0) { std::cout << "\n there are no duplicate files in directory rooted at"; std::cout << "\n " << FileSystem::Path::getFullFileSpec(startPath); return 0; } for (auto fileSpec : dupFiles) { std::cout << "\n " << fileSpec.first; for (auto pathIter : fileSpec.second) { Duplicates::FilePath path = *pathIter; std::cout << "\n " << path; } } std::cout << std::endl; std::cout << "\n Total number of duplicate files = " << dupFiles.size(); std::cout << "\n Total number of paths = " << dups.filePaths().size(); std::cout << "\n\n"; }