The code takes in two arguments, a path to search and an output file name. It searches the supplied path (including sub directories) and writes out all files that have the same size as any other files in the path. This will form part of a multi-step process to identify duplicate files within the file system.
Example output, with 3 files 29 bytes long, 3 files 113 bytes long and 2 files 114 bytes long:
29 c:\Program Files\Git\mingw64\libexec\git-core\mergetools\gvimdiff 29 c:\Program Files\Git\mingw64\libexec\git-core\mergetools\gvimdiff2 29 c:\Program Files\Git\mingw64\libexec\git-core\mergetools\gvimdiff3 113 c:\Program Files\Git\mingw64\lib\tcl8.6\tzdata\Etc\GMT-7 113 c:\Program Files\Git\mingw64\lib\tcl8.6\tzdata\Etc\GMT-8 113 c:\Program Files\Git\mingw64\lib\tcl8.6\tzdata\Etc\GMT-9 114 c:\Program Files\Git\mingw64\lib\tcl8.6\tzdata\Etc\GMT+4 114 c:\Program Files\Git\mingw64\lib\tcl8.6\tzdata\Etc\GMT+5
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <filesystem>
#include <map>
#include <set>
using namespace std::experimental::filesystem;
std::map<uintmax_t, std::set<path>> build_filesize_map(const std::string &pathToSearch);
void write_possible_duplicate_files(const std::string &outputFileName, const std::map<uintmax_t, std::set<path>> &sizeMap);
int main(int argc, char *argv[])
{
if (argc != 3) {
std::cout << "Usage:" << argv[0] << " <folder to scan> <output file>";
return EXIT_FAILURE;
}
std::string pathToSearch(argv[1]);
std::string outputFileName(argv[2]);
auto sizeMap = build_filesize_map(pathToSearch);
// Write out a list of all files that share their size with other files
write_possible_duplicate_files(outputFileName, sizeMap);
}
// Search directory, any files with size > 0 are considered. They are added to a set
// within the map, which is keyed on the file size
std::map<uintmax_t, std::set<path>> build_filesize_map(const std::string &pathToSearch) {
auto sizeMap = std::map<uintmax_t, std::set<path>>();
for (recursive_directory_iterator next(pathToSearch), end; next != end; ++next) {
auto currentPath = next->path();
auto currentFileSize = is_regular_file(currentPath) ? file_size(currentPath) : 0;
if (0 != currentFileSize) {
auto existingEntry = sizeMap.find(currentFileSize);
if (existingEntry == sizeMap.end()) {
std::set<path> fileSet;
fileSet.emplace(currentPath);
sizeMap.emplace(currentFileSize, fileSet);
}
else {
existingEntry->second.emplace(currentPath);
}
}
}
return sizeMap;
}
// Write out a list of all files that share their size with other files
void write_possible_duplicate_files(const std::string &outputFileName, const std::map<uintmax_t, std::set<path>> &sizeMap) {
std::ofstream outputFile(outputFileName, std::ios_base::trunc);
for (auto sizeIterator = sizeMap.begin(); sizeIterator != sizeMap.end(); ++sizeIterator) {
if (sizeIterator->second.size() == 1) continue;
for (auto pathIteror = sizeIterator->second.begin(); pathIteror != sizeIterator->second.end(); ++pathIteror) {
outputFile << sizeIterator->first << "\t" << *pathIteror << std::endl;
}
}
}
Things I'm particularly interested in (anything else is welcome):
- Naming - I generally write C# these days and everywhere I've written C++ has had its own naming convention. Is there an industry standard or is consistency still the most important convention?
- STL usage - I've used Boost, RogueWave and lots of custom classes but have little experience with the STL. Specifically, I'm adding local instance to the collections, rather than
new
ing them up. Is that OK, or should I be adding pointers instead? - My
build_filesize_map
is returning amap
instance. Is that OK? Historically, I'd have new'd up the map and returned a pointer to it. Which is the better approach?