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

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.

Related

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.

Find all files in all directories in 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;
}

Select the last modified file from a directory

I need to know, how I can select the Last modified/created file in a given directory.
I currently have a directory named XML, and inside that there are many XML files. But I would like to select only the last modified file.
I use the following function to list all the items inside a folder. It writes all the files in a string vector, but you can change that.
bool ListContents (vector<string>& dest, string dir, string filter, bool recursively)
{
WIN32_FIND_DATAA ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
DWORD dwError = 0;
// Prepare string
if (dir.back() != '\\') dir += "\\";
// Safety check
if (dir.length() >= MAX_PATH) {
Error("Cannot open folder %s: path too long", dir.c_str());
return false;
}
// First entry in directory
hFind = FindFirstFileA((dir + filter).c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
Error("Cannot open folder in folder %s: error accessing first entry.", dir.c_str());
return false;
}
// List files in directory
do {
// Ignore . and .. folders, they cause stack overflow
if (strcmp(ffd.cFileName, ".") == 0) continue;
if (strcmp(ffd.cFileName, "..") == 0) continue;
// Is directory?
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// Go inside recursively
if (recursively)
ListContents(dest, dir + ffd.cFileName, filter, recursively, content_type);
}
// Add file to our list
else dest.push_back(dir + ffd.cFileName);
} while (FindNextFileA(hFind, &ffd));
// Get last error
dwError = GetLastError();
if (dwError != ERROR_NO_MORE_FILES) {
Error("Error reading file list in folder %s.", dir.c_str());
return false;
}
return true;
}
(don't forget to include windows.h)
What you have to do is adapt it to find the newest file.
The ffd structure (WIN32_FIND_DATAA data type) contains ftCreationTime, ftLastAccessTime and ftLastWriteTime, you can use those to find the newest file.
These members are FILETIME structures, you can find the documentation here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284%28v=vs.85%29.aspx
You can use FindFirstFile and FindNextFile, they deliver a struct describing the file like size as well as modified time.
Boost.Filesystem offers last_write_time. You can use this to sort the files in a directory. Boost.Filesystem and (Boost) in general can be a little bit intimidating for a C++ new-comer so you might want to check a solution for your OS first.

Problems with Visual C++: Reading all files in a directory

I'm trying to read all files in a directory. I have the following code:
void scanDirectory(char* dir)
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind = INVALID_HANDLE_VALUE;
char DirSpec[MAX_PATH]; // directory specification
strcpy(DirSpec, dir);
strcat(DirSpec, "\\*");
hFind = FindFirstFile(DirSpec, &FindFileData);
int i = 0;
do {
i++;
printf("%d \n", i);
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
printf(" %s <DIR>\n", FindFileData.cFileName);
}
else
{
printf("File %s\n", FindFileData.cFileName);
}
} while(!FindNextFile(hFind, &FindFileData));
FindClose(hFind);
}
The problem is that when I execute the code it results in an infinite loop. Also the output characters are strange, like "File ".
I think you are not using chars and wide chars in a consequent way. You should either use functions with wide char and wchar_t type or vice versa. (But it was a compile error for me so it may depend on some kind of project settings as well.)
And your exit condition in the while loop is also wrong it should test for FindNextFile and not !FindNextFile. The infinite loop may be because of this condition as if it doesn't find any files it will run forever.
Also you should test for the return value of FindFirstFile and not go into the loop if it doesn't find any files.
You are calling !FindNextFile instead of FindNextFile, also you are not checking why
the FindNextFile fails, so you can't be sure if all the files were processed.
Use something like this.
WIN32_FIND_DATA stFindData;
HANDLE hFind = FindFirstFile(cSearchPattern, &stFindData);
if(hFind != INVALID_HANDLE_VALUE)
{
do
{
// Process File
}
while (FindNextFile(hFind, &stFindData) != 0);
DWORD dwError = GetLastError();
if (dwError != ERROR_NO_MORE_FILES)
{
// Not All Files processed, deal with Error
}
FindClose(hFind);
}
Can't you just use .Net like below:
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(Path);
System.IO.FileInfo[] files = dir.GetFiles();
foreach (System.IO.FileInfo file in files)
{
// Do whatever you need with the file info...
string filename = file.Name;
string fullFilename = file.FullName;
}
This is a c# example but you can use for each in C++ the same. Hope this helps.

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.