Find all files in all directories in c++ - c++

I am trying to find all files in all directories, but I don't know how to handle subdirectories. In this code, the code looks trough all subdirs, but I don't know how to jump back. Does anyone know how to do this?
__declspec(dllexport) void GetFiles(char* filedir, char* path)
{
string s[1000];
string path2 = path;
UINT index = 0;
WIN32_FIND_DATA ffd;
TCHAR szDir[MAX_PATH];
HANDLE hFind = INVALID_HANDLE_VALUE;
DWORD dwError=0;
StringCchCopy(szDir, MAX_PATH, filedir);
if (INVALID_HANDLE_VALUE == hFind)
return;
do
{
DWORD attributes = ffd.dwFileAttributes;
if (attributes & FILE_ATTRIBUTE_HIDDEN)
continue;
else if (attributes & FILE_ATTRIBUTE_DIRECTORY)
{
TCHAR dir2[MAX_PATH];
path2 = path;
path2 += ffd.cFileName;
path2 += "\\*";
StringCchCopy(dir2, MAX_PATH, path2.c_str());
SetCurrentDirectory(dir2);
}
else
{
s[index] = path;
s[index] += ffd.cFileName;
index++;
}
}
while (FindNextFile(hFind, &ffd) >= 0); // needs to jump back if zero
FindClose(hFind);
}
EDIT: functions had the same name which confused the compiler

I think the easiest way to do it is by doing a recursive function.
This would roughly look like something like this in "c" pseudo code
void GetFiles( char*** file_path_table, char* dir )
{
char **file_paths;
file_paths = getAllFiles( dir );
foreach( path in file_paths )
{
if ( is_directory( path ) )
{
GetFiles( file_path_table, path );
}
else
{
add_file_to_table( file_path_table, path );
}
}
}

Why not use the boost recursive_directory_iterator.
Note: untested (but should look something like this).
namespace bfs = boost::filesystem;
std::vector<std::string> filenames;
std::copy(bfs::recursive_directory_iterator("<path>"),
bfs::recursive_directory_iterator(),
std::back_inserter(filenames)
);

Instead of changing directory via SetCurrentDirectory() use a recursive call on GetFiles(). This would require that the caller pass in a reference to an array (or std::vector<std::string>) for the list of files to be stored in instead of using the local array s.

I'd have a look at the directory iterators of boost instead.
http://www.boost.org/doc/libs/1_51_0/libs/filesystem/doc/index.htm
There are examples covering what you are trying to do, and it will work for almost any OS you can think of.
Have a look at example 3. It shows how to loop over all contents of the directory. If you find a new directory you have not seen before, you just do the same on that. There are tests telling you if the file is regular, directory etc so give it a try.

Doing a bit of searching through old posts, I guess I've mentioned doing a breadth-first search a number of times, but never really posted code to show how to do it. I guess I might as well do that.
#include <windows.h>
#include <queue>
#include <string>
#include <iostream>
// I think MS's names for some things are obnoxious.
const HANDLE HNULL = INVALID_HANDLE_VALUE;
const int A_DIR = FILE_ATTRIBUTE_DIRECTORY;
// We'll process a file by printing its path/name
void process(std::string const &path, WIN32_FIND_DATA const &file) {
std::cout << path << file.cFileName << "\n";
}
void find_file(std::string const &folder_name, std::string const &fmask) {
HANDLE finder; // for FindFirstFile
WIN32_FIND_DATA file; // data about current file.
std::priority_queue<std::string, std::vector<std::string>,
std::greater<std::string> > dirs;
dirs.push(folder_name); // start with passed directory
do {
std::string path = dirs.top();// retrieve directory to search
dirs.pop();
if (path[path.size()-1] != '\\') // normalize the name.
path += "\\";
std::string mask = path + fmask; // create mask for searching
// traverse a directory. Search for sub-dirs separately, because we
// don't want a mask to apply to directory names. "*.cpp" should find
// "a\b.cpp", even though "a" doesn't match "*.cpp".
//
// First search for files:
if (HNULL==(finder=FindFirstFile(mask.c_str(), &file)))
continue;
do {
if (!(file.dwFileAttributes & A_DIR))
process(path, file);
} while (FindNextFile(finder, &file));
FindClose(finder);
// Then search for subdirectories:
if (HNULL==(finder=FindFirstFile((path + "*").c_str(), &file)))
continue;
do {
if ((file.dwFileAttributes & A_DIR) && (file.cFileName[0] != '.'))
dirs.push(path + file.cFileName);
} while (FindNextFile(finder, &file));
FindClose(finder);
} while (!dirs.empty());
}
int main(int argc, char **argv) {
if (argc > 2)
find_file(argv[1], argv[2]);
else
find_file("C:\\", "*");
return 0;
}

Related

How do I get appdata/local/<MyProgramFolder>/ path in c++

I used fstream and used getenv to get the path of appdata. I used the following codes but it output on appdata/roaming/
QString appdata = getenv("appdata");
appdata += "\\DoDLog.log";
fstream stud;
stud.open(appdata.toStdString().c_str(), ios::app);
What I want to happen is the path of appdata/local//DoDLog.log
MyProgram is the c++ program that i've been running. Please help.
You need to use SHGetSpecialFolderLocation
This is a C solution, it shouldn't be hard to adapt it to C++, QString, std::string or whatever.
#include <shlobj.h>
...
HRESULT GetFolderLocation (int csidl, char* buffer)
{
LPITEMIDLIST pidl = 0;
HRESULT result = SHGetSpecialFolderLocation(NULL, csidl, &pidl);
*buffer = 0 ;
if (result == 0)
{
SHGetPathFromIDList(pidl, buffer);
CoTaskMemFree(pidl);
}
return result;
}
...
char str[_MAX_PATH];
GetFolderLocation(CSIDL_LOCAL_APPDATA, str);
// now str contains "C:\Users\<user>\AppData\Local"
...

How to find a list of files in a specific directory without dirent.h or boost [duplicate]

I'm looking to list and store the contents of a directory in a struct using C on Windows.
I'm not necessarily looking for anyone to write out the code I'm looking for, rather point me in the right direction when it comes to which library I should be looking at.
I've been Googling for a few hours now and all I'm finding is C#, C++ solutions so any help would be greatly appreciated.
Just like everyone else said (with FindFirstFile, FindNextFile and FindClose)... but with recursion!
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
bool ListDirectoryContents(const char *sDir)
{
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;
char sPath[2048];
//Specify a file mask. *.* = We want everything!
sprintf(sPath, "%s\\*.*", sDir);
if((hFind = FindFirstFile(sPath, &fdFile)) == INVALID_HANDLE_VALUE)
{
printf("Path not found: [%s]\n", sDir);
return false;
}
do
{
//Find first file will always return "."
// and ".." as the first two directories.
if(strcmp(fdFile.cFileName, ".") != 0
&& strcmp(fdFile.cFileName, "..") != 0)
{
//Build up our file path using the passed in
// [sDir] and the file/foldername we just found:
sprintf(sPath, "%s\\%s", sDir, fdFile.cFileName);
//Is the entity a File or Folder?
if(fdFile.dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY)
{
printf("Directory: %s\n", sPath);
ListDirectoryContents(sPath); //Recursion, I love it!
}
else{
printf("File: %s\n", sPath);
}
}
}
while(FindNextFile(hFind, &fdFile)); //Find the next file.
FindClose(hFind); //Always, Always, clean things up!
return true;
}
ListDirectoryContents("C:\\Windows\\");
And now its UNICODE counterpart:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
bool ListDirectoryContents(const wchar_t *sDir)
{
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;
wchar_t sPath[2048];
//Specify a file mask. *.* = We want everything!
wsprintf(sPath, L"%s\\*.*", sDir);
if((hFind = FindFirstFile(sPath, &fdFile)) == INVALID_HANDLE_VALUE)
{
wprintf(L"Path not found: [%s]\n", sDir);
return false;
}
do
{
//Find first file will always return "."
// and ".." as the first two directories.
if(wcscmp(fdFile.cFileName, L".") != 0
&& wcscmp(fdFile.cFileName, L"..") != 0)
{
//Build up our file path using the passed in
// [sDir] and the file/foldername we just found:
wsprintf(sPath, L"%s\\%s", sDir, fdFile.cFileName);
//Is the entity a File or Folder?
if(fdFile.dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY)
{
wprintf(L"Directory: %s\n", sPath);
ListDirectoryContents(sPath); //Recursion, I love it!
}
else{
wprintf(L"File: %s\n", sPath);
}
}
}
while(FindNextFile(hFind, &fdFile)); //Find the next file.
FindClose(hFind); //Always, Always, clean things up!
return true;
}
ListDirectoryContents(L"C:\\Windows\\");
Probably You are looking for these functions: FindFirstFile, FindNextFile, and FindClose.
To list file contents you can search a directory with these APIs: FindFirstFileEx, FindNextFile and FindClose. You'll need to #include <windows.h>, that'll get you access to the Windows API. They're C functions and so compatible with C++. If you want "specifically C++", try searching for listing directories using MFC.

How to handle Symbolic Links and Junction while deleting or coping a folder tree

I had task to copy and delete a huge folder using win32 api (C++), I am using the Code Guru recurisive directory deletion code, which works well, but there arises certain question.
RemoveDirectory
Million thanks to Lerooooy Jenkins for pointing it.
The link to CodeGuru for recursive deletes doesn't correctly handle
symbolic links/junctions. Given that a reparse point could point
anywhere (even network drives), you need to be careful when deleting
recursively and only delete the symbolic link/junction and not what it
points at. The correct way to handle this situation is to detect
reparse points (via GetFileAttributes()) and NOT traverse it as a
subdirectory.
So my question is how to actually handle Symbolic Links and Junction while deleting or coping a folder tree.
For the shake of question here is the source code of CodeGuru Directory Deletion
#include <string>
#include <iostream>
#include <windows.h>
#include <conio.h>
int DeleteDirectory(const std::string &refcstrRootDirectory,
bool bDeleteSubdirectories = true)
{
bool bSubdirectory = false; // Flag, indicating whether
// subdirectories have been found
HANDLE hFile; // Handle to directory
std::string strFilePath; // Filepath
std::string strPattern; // Pattern
WIN32_FIND_DATA FileInformation; // File information
strPattern = refcstrRootDirectory + "\\*.*";
hFile = ::FindFirstFile(strPattern.c_str(), &FileInformation);
if(hFile != INVALID_HANDLE_VALUE)
{
do
{
if(FileInformation.cFileName[0] != '.')
{
strFilePath.erase();
strFilePath = refcstrRootDirectory + "\\" + FileInformation.cFileName;
if(FileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if(bDeleteSubdirectories)
{
// Delete subdirectory
int iRC = DeleteDirectory(strFilePath, bDeleteSubdirectories);
if(iRC)
return iRC;
}
else
bSubdirectory = true;
}
else
{
// Set file attributes
if(::SetFileAttributes(strFilePath.c_str(),
FILE_ATTRIBUTE_NORMAL) == FALSE)
return ::GetLastError();
// Delete file
if(::DeleteFile(strFilePath.c_str()) == FALSE)
return ::GetLastError();
}
}
} while(::FindNextFile(hFile, &FileInformation) == TRUE);
// Close handle
::FindClose(hFile);
DWORD dwError = ::GetLastError();
if(dwError != ERROR_NO_MORE_FILES)
return dwError;
else
{
if(!bSubdirectory)
{
// Set directory attributes
if(::SetFileAttributes(refcstrRootDirectory.c_str(),
FILE_ATTRIBUTE_NORMAL) == FALSE)
return ::GetLastError();
// Delete directory
if(::RemoveDirectory(refcstrRootDirectory.c_str()) == FALSE)
return ::GetLastError();
}
}
}
return 0;
}
int main()
{
int iRC = 0;
std::string strDirectoryToDelete = "c:\\mydir";
// Delete 'c:\mydir' without deleting the subdirectories
iRC = DeleteDirectory(strDirectoryToDelete, false);
if(iRC)
{
std::cout << "Error " << iRC << std::endl;
return -1;
}
// Delete 'c:\mydir' and its subdirectories
iRC = DeleteDirectory(strDirectoryToDelete);
if(iRC)
{
std::cout << "Error " << iRC << std::endl;
return -1;
}
// Wait for keystroke
_getch();
return 0;
}
Use DeleteFile to delete file symbolic links.
Use RemoveDirectory to delete directory symbolic links and junctions.
In other words, you treat them just like any other file or directory except that you don't recurse into directories that have the FILE_ATTRIBUTE_REPARSE_POINT attribute.
The simplest way to achieve your goal, and the recommended way to do it, is to get the system to do the work.
If you need to support XP then you use SHFileOperation with the FO_DELETE flag.
Otherwise, for Vista and later, use IFileOperation.
These APIs handle all the details for you, and use the same code paths as does the shell. You can even show the standard shell progress UI if you desire.

List files in directory C++

I'm trying to use this example
to create a program which will list all filenames in a directory in a Windows::Forms::ListBox.
Since the user won't be doing any inputting I won't be needing the
void DisplayErrorBox(LPTSTR lpszFunction) function along with other error checking.
When I click the button that triggers the event this is what is shown in the list box.
o //&#o/
/ //
/ //
/ //
/ //
/ //
/ //
/ //
Also, only one row appear each time i click the button.
It's supposed to find all the files in the directory and list them not just find the next file each time I click the button.
I also want to use a relative strPath, not absolute...
So far this is what I've done with the code:
private:
void List_Files()
{
std::string strPath = "C:\\Users\\Andre\\Dropbox\\Programmering privat\\Diablo III DPS Calculator\\Debug\\SavedProfiles";
TCHAR* Path = (TCHAR*)strPath.c_str();
WIN32_FIND_DATA ffd;
LARGE_INTEGER filesize;
TCHAR szDir[MAX_PATH];
size_t length_of_arg;
HANDLE hFind = INVALID_HANDLE_VALUE;
// Prepare string for use with FindFile functions. First, copy the
// string to a buffer, then append '\*' to the directory name.
StringCchCopy(szDir, MAX_PATH, Path);
StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
// List all the files in the directory with some info about them.
do
{
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
//If it's a directory nothing should happen. Just continue with the next file.
}
else
{
//convert from wide char to narrow char array
char ch[260];
char DefChar = ' ';
WideCharToMultiByte(CP_ACP,0,(ffd.cFileName),-1, ch,260,&DefChar, NULL);
//A std:string using the char* constructor.
std::string str(ch);
String ^ sysStr = gcnew String(str.c_str());
MessageBox::Show("File Found", "NOTE");
ListBoxSavedFiles->Items->Add (sysStr);
}
}
while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
}
FindFirstFile() is never called, you need to call it before calling FindNextFile():
HANDLE hFind = FindFirstFile(TEXT("C:\\Users\\Andre\\Dropbox\\Programmering privat\\Diablo III DPS Calculator\\Debug\\SavedProfiles\\*"), &ffd);
if (INVALID_HANDLE_VALUE != hFind)
{
do
{
//...
} while(FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
}
else
{
// Report failure.
}
If you do not mind using Boost, you can use a directory_iterator:
using boost::filesystem;
path p("some_dir");
for (directory_iterator it(p); it != directory_iterator(); ++it) {
cout << it->path() << endl;
}
It works on Windows too and it definitely looks much simpler. Of course, you would need to adapt your current code a little bit, but I think in the long term it is well worth the effort.
The cast (TCHAR*)strPath.c_str(); is wrong. From your use of WideCharToMultiByte I know that (TCHAR*)strPath.c_str(); is casting a char const* to a wchar_t*. Not only does that lose a const, but the width is also wrong.
If u are using Visual Studio then change the configuration settings to Use Multibyte Character set. This will your TCHAR thing to compile without any cast.

File count in a directory using C++

How do I get the total number of files in a directory by using C++ standard library?
If you don't exclude the basically always available C standard library, you can use that one.
Because it's available everywhere anyways, unlike boost, it's a pretty usable option!
An example is given here.
And here:
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main (void)
{
DIR *dp;
int i = 0;
struct dirent *ep;
dp = opendir ("./");
if (dp != NULL)
{
while (ep = readdir (dp))
i++;
(void) closedir (dp);
}
else
perror ("Couldn't open the directory");
printf("There's %d files in the current directory.\n", i);
return 0;
}
And sure enough
> $ ls -a | wc -l
138
> $ ./count
There's 138 files in the current directory.
This isn't C++ at all, but it is available on most, if not all, operating systems, and will work in C++ regardless.
UPDATE: I'll correct my previous statement about this being part of the C standard library - it's not. But you can carry this concept to other operating systems, because they all have their ways of dealing with files without having to grab out additional libraries.
EDIT: : Added initialization of i
You can't. The closest you are going to be able to get is to use something like Boost.Filesystem
EDIT: It is possible with C++17 using the STL's filesystem library
As of C++17 it can be done with STL:
auto dirIter = std::filesystem::directory_iterator("directory_path");
int fileCount = std::count_if(
begin(dirIter),
end(dirIter),
[](auto& entry) { return entry.is_regular_file(); }
);
A simple for-loop works, too:
auto dirIter = std::filesystem::directory_iterator("directory_path");
int fileCount = 0;
for (auto& entry : dirIter)
{
if (entry.is_regular_file())
{
++fileCount;
}
}
See https://en.cppreference.com/w/cpp/filesystem/directory_iterator
An old question, but since it appears first on Google search, I thought to add my answer since I had a need for something like that.
int findNumberOfFilesInDirectory(std::string& path)
{
int counter = 0;
WIN32_FIND_DATA ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
// Start iterating over the files in the path directory.
hFind = ::FindFirstFileA (path.c_str(), &ffd);
if (hFind != INVALID_HANDLE_VALUE)
{
do // Managed to locate and create an handle to that folder.
{
counter++;
} while (::FindNextFile(hFind, &ffd) == TRUE);
::FindClose(hFind);
} else {
printf("Failed to find path: %s", path.c_str());
}
return counter;
}
If they are well named, sorted, and have the same extension, you could simply do count them with standard C++ library.
Assume the file names are like "img_0.jpg..img_10000.jpg..img_n.jpg",
Just check if they are in the folder or not.
int Trainer::fileCounter(string dir, string prefix, string extension)
{
int returnedCount = 0;
int possibleMax = 5000000; //some number you can expect.
for (int istarter = 0; istarter < possibleMax; istarter++){
string fileName = "";
fileName.append(dir);
fileName.append(prefix);
fileName.append(to_string(istarter));
fileName.append(extension);
bool status = FileExistenceCheck(fileName);
returnedCount = istarter;
if (!status)
break;
}
return returnedCount;
}
bool Trainer::FileExistenceCheck(const std::string& name) {
struct stat buffer;
return (stat(name.c_str(), &buffer) == 0);
}
You would need to use a native API or framework.