Finding the file path of all files in a folder - c++

I'm trying to convert a bunch of images to textures using SDL. So far, I know its possible to do everything manually:
//Load front alpha texture
if (!gModulatedTexture.loadFromFile("14_animated_sprites_and_vsync/text2.png"))
{
printf("Failed to load front texture!\n");
success = false;
}
else
.....
However, I have quite a few images I want to load so what I'm looking for is a way to automate the process. I want to put all my images into a single folder, and then do something like this:
i=0
while (there are still images to load) {
textureBank[i] = current image
i++
}
I wast thinking there might be some easy way to just read in the file path of all the files in a directory, but I haven't been able to find a way to do that.
Any suggestions?

You don't need to use any 3rd-party library like boost, just call the following function (for Windows OS). After this, you will get all file paths within given folder in vector<string>.
#include <Windows.h>
// folder must end with "/", e.g. "D:/images/"
vector<string> get_all_files_full_path_within_folder(string folder)
{
vector<string> names;
char search_path[200];
sprintf(search_path, "%s*.*", folder.c_str());
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(search_path, &fd);
if(hFind != INVALID_HANDLE_VALUE)
{
do
{
// read all (real) files in current folder, delete '!' read other 2 default folder . and ..
if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
{
names.push_back(folder+fd.cFileName);
}
}while(::FindNextFile(hFind, &fd));
::FindClose(hFind);
}
return names;
}

Since you are using SDL, I’ll assume you want to be cross-platform. The boost::filesystem library can do this.
Take a look at their directory iteration example.
Although it’s part of a 3rd-party library, boost::filesystem is proposed for inclusion in a future C++ standard, TR2, so it’s worth the effort to learn. It should eventually be the standard C++ way to work with files and directories.

Related

How to properly navigate directory paths in C++

I'm working on a solution within Visual Studio. It currently has two projects.
I will represent Directories or folders with capitals letters, and filenames will be all lower case. My solution structure is as follows:
SolutionDir
ProjectLib
source files
Shaders
shader files
ProjectApp
source files
x64
Debug
app.exe // debug build
Release
app.exe // release build
Within ProjectLib I have a function to open and read my Shader files. Here is what my function looks like:
std::vector<char> VRXShader::readFile(std::string_view shadername) {
std::string filename = std::string("Shaders/");
filename.append(shadername);
std::ifstream file(filename.data(), std::ios::ate | std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error("failed to open file!");
}
size_t fileSize = static_cast<size_t>(file.tellg());
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
This function is being called within my VRXDevices::createPipeline function and here is the relevant code:
void VRXDevices::createPipeline(
VkDevice device, VkExtent2D swapChainExtent, VkRenderPass renderPass,
const std::vector<std::string_view>& shaderNames,
VkPipelineLayout& pipelineLayout, VkPipeline& pipeline
) {
std::vector<std::vector<char>> shaderCodes;
shaderCodes.resize(shaderNames.size());
for (auto& name : shaderNames) {
auto shaderCode = VRXShader::readFile(name.data());
}
// .... more code
}
The names are being created and passed to this function from my VRXEngine::initVulkan function which can be seen here:
void VRXEngine::initVulkan(
std::string_view app_name, std::string_view engine_name,
glm::ivec3 app_version, glm::ivec3 engine_version
) {
//... code
std::vector<std::string_view> shaderFilenames{ "vert.spv", "frag.spv" };
VRXDevices::createPipeline(device_, swapChainExtent_, renderPass_, shaderFilenames, pipelineLayout_, graphicsPipeline_);
}
I'm using just the name of the shader files such as vert.spv, frag.spv, geom.spv etc. I'm not including the paths here because these will be used as the key to a std::map<string_view, object>. So I'm passing a vector of these names from my ::initVulkan function into ::createPipeline().
Within ::createPipeline() is where ::readFile() is being called passing in the string_view.
Now as for my question... within ::readFile() I'm creating a local string and trying to initialize it with the appropriate path... then append to it the string_view for the shader's filename as can be seen from these two lines...
std::string filename = std::string("Shaders/");
filename.append(shadername);
I'm trying to figure out the appropriate string to initialize filename with... Shaders/ will be a part of the name, but it's not finding the file and I'm not sure what the appropriate prefix should be...
My working directories within both projects are as follows:
ProjectApp -> $(SolutionDir)x64/Release AND $(SolutionDir)x64/Debug
ProjectLib -> $(SolutionDir)x64/Release AND $(SolutionDir)x64/Debug
So I need to go back 2 directories then into VRX Engine/Shader...
What is the correct string value for navigating back directories?
Would I initialize filename with "../../VRX Engine/Shaders/" or is it "././" also, should I have quotes around VRX Engine since there is a space in the folder name? What do I need to initialize filename with before I append the shader name to it?
How to properly navigate directory paths in C++
It depends on which C++ standard your implementation claims to be compliant with.
Or else which additional libraries can you use.
C++ is useful on computers without directories (e.g. inside some operating system kernel coded in C++ and compiled with GCC, see OSDEV for examples).
Look on en.cppreference.com for details.
Licensing constraints could matter when using extra open source libraries.
If your implementation is C++17 compliant (in a "hosted" not "freestanding" way), use the std::filesystem part of the standard library.
If your operating system supports the Qt or POCO frameworks and you are allowed to use them (e.g. on C++11), you could use appropriate APIs. So QDir and related classes with Qt, Poco::Path and related classes with POCO.
Perhaps you want to code just for the WinAPI. Then read its documentation (I never coded on Windows myself, just on POSIX or Unix -e.g. Linux- and MSDOS....).
I was originally initializing my local temp string properly with "../../VRX Engine/Shaders/" before appending the string_view to it to be able to open the file. This was actually correct, but because it didn't initially work, I was assuming that it was wrong.
The correct string value for going back one directory should be "../" at least on Windows, I'm not sure about Linux, Mac, Android, etc...
My problem wasn't with the string at all, it pertained to settings within my projects. Within my project that builds into an executable, I had its working directory set to $(SolutionDir)x64/Debug and $(SolutionDir)x64/Release respectively which is correct for my solutions structure.
The issue was within my Engine project that is being built as a static library. Within its settings for its working directory, I had forgotten to modify both of the Debug and Release build options... These were still set to the default values of Visual Studio which I believe is (ProjectDir). Once I changed these to $(SolutionDir)x64/Debug and $(SolutionDir)x64/Release to match that of my ApplicationProject, I was able to open and read the contents of the files.

How to walk through directory tree step by step?

I found many examples on walking through directory tree, but I need something a little different. I need a class with some method which each call returns one file from directory and gradually walking through directory tree. How can I do this please? I am using functions FindFirstFile, FindNextFile and FindClose, I am newbie in c++. I have something like this...
For example I have this simple directory tree
Parent(folder)\
file1.txt
file2.txt
Child(folder)\
file3.txt
file4.txt
and I need a class with a method for example getNextFile(), that first call returns file1.txt; second call returns file2.txt, third call returns Child(folder), fourth call returns file3.txt and so on...
Edit on duplicate flag: I basically need walk through tree without do/while, while or for...I need some kind of iterator, which can be stored for later use and which can continue from last file, when I interrupt browsing, but ideally only with using winapi calls
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;
if((hFind = FindFirstFile(sPath, &fdFile)) == INVALID_HANDLE_VALUE)
{
return false;
}
do
{
//do some job with fdFile
}
while(FindNextFile(hFind, &fdFile));
Here is the native C++ way of doing it on Windows platform (using MFC framework):
void ListFiles(const CString& sPath)
{
CFileFind finder;
CString sWildcard(sPath);
sWildcard += _T("\\*.*");
BOOL bWorking = finder.FindFile(sWildcard);
while (bWorking)
{
bWorking = finder.FindNextFile();
if (finder.IsDots())
continue;
if (finder.IsDirectory())
{
CString sFilePath = finder.GetFilePath();
// TODO: do stuff here
ListFiles(sFilePath);
}
}
finder.Close();
}
You can change wild card string to target specific files, like *.txt etc. You can also pass it as a parameter to this function to make it more general purpose.
Use the right tools. Boost is available as good as everywhere, and has the methods you want.
From http://rosettacode.org/wiki/Walk_a_directory/Recursively#C.2B.2B:
#include "boost/filesystem.hpp"
#include "boost/regex.hpp"
#include <iostream>
using namespace boost::filesystem;
int main()
{
path current_dir("."); //
boost::regex pattern("a.*"); // list all files starting with a
for (recursive_directory_iterator iter(current_dir), end;
iter != end;
++iter)
{
std::string name = iter->path().filename().string();
if (regex_match(name, pattern))
std::cout << iter->path() << "\n";
}
}
remove the whole regex business if you don't care whether your file matches a certain pattern.
EDIT:
Could you please explain why it would be bad to use directly API calls ?
it's ugly and hard to read, even harder to get right,
it's not portable at all, and what's most important,
there's a million corner cases you'd have to take care of, possibly, when using the raw win api. Boost has been written by people who did this a few hundred times and has underwent serious code review, so take the save route, and don't reinvent a wheel.
In essence, winapi is about two decades old; there's been a lot of usability improvement in the rest of the world. Unless you have a really good reason, I would try to abstract as much of it away as possible by using common libraries, such as Boost.
I think this does not solves my problem, I edited the original post to make it clearer.
basically need walk through tree without do/while, while or for...I need some kind of iterator, which can be stored for later use
That's exactly what my answer does: give you an Iterator in a for loop. I don't understand what's not fulfilling your Edit's specification about that.
In addition, it would be best to use only WinAPI, because it has to work on different computers with windows and installing boost could be a problem.
You don't have to install boost on any of these computers. Boost::filesystem can comfortable be linked in statically; also, the old-school windows way of doing this is just delivering boost_filesystem*.dll and boost_system*.dll along with your binary. However, if your goal is a single executable that contains all needed functions, you'll go for static linkage, anyway, so this is absolutely no problem.

C++ folder wont delete until I close program

In a game I'm making, folders with text files inside represent world saves, In the load menu of this game I want to have an option to delete a save. I'm using currently this code to try to delete the saves:
hFind = FindFirstFile((dir+"/*").c_str(), &FindFileData);
if (hFind){
do{
string s = FindFileData.cFileName;
if(s.find('.')){//prevents prossesing of "." and ".."
DeleteFile((dir+"/"+s).c_str());
}
}while(FindNextFile(hFind,&FindFileData));
CloseHandle(hFind);
}
rmdir(dir.c_str());
The only things in these folders are 3 text files so this code should be sufficient, however it isn't. What happens is all the file in the directory are deleted but not the folder, and if I try to delete this folder manually, or edit it in any way while the program is running, windows denies me access. But as soon as I close the game the folder is deleted.
I know the files inside are deleted because I tried the above code with out "rmdir(dir.c_str());" and opened the folder and all the files were gone, also with the above code if I "Delete" the save and then try to load it I there is no world and no inventory, indicating the files have been deleted.
I've tried it with removeDirectory and the same thing happens, It also says it was deleted successfully without any errors.
Why does this happen? How can I avoid this, and get it to work properly?
Any help would be greatly appreciated.
The problem was fixxed with the following code:
hFind = FindFirstFile((dir+"/*").c_str(), &FindFileData);
if (hFind){
do{
string s = FindFileData.cFileName;
if(s.find('.')){//prevents prossesing of "." and ".."
DeleteFile((dir+"/"+s).c_str());
}
}while(FindNextFile(hFind,&FindFileData));
CloseHandle(hFind);
}
findClose(hFind);
rmdir(dir.c_str());
According to the RemoveDirectory documentation:
RemoveDirectory function marks a directory for deletion on close. Therefore, the directory is not removed until the last handle to the directory is closed.
Probably your program has the directory as its current working directory, or perhaps otherwise still has a handle to it open.
In windows, rmdir is a comparability function that calls the native windows functions, so it will behave the same.
The root problem is that the code called CloseHandle instead of FindClose on the handle returned by FindFirstFile.
But the code has several more bugs. In the interest of helping future visitors here, this is the corrected code.
HANDLE hFind = FindFirstFile((dir + "\\*").c_str(), &FindFileData); // See 1 below
if (hFind != INVALID_HANDLE_VALUE) { // 2
do {
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // 3
const std::string s = dir + "\\" + FindFileData.cFileName;
DeleteFile(s.c_str());
}
} while (FindNextFile(hFind, &FindFileData));
// 4
FindClose(hFind); // 5
}
RemoveDirectory(dir.c_str()); // 6
Windows paths use \ rather than / as separators. Many of the APIs will accept either, but eventually you'll encounter one that doesn't, so it's best to use the correct one consistently.
FindFirstFile returns INVALID_HANDLE_VALUE (not NULL) upon failure. Since INVALID_HANDLE_VALUE is non-zero, you cannot simply test if (hFile) { ... }.
The API enumerates files and directories. The old code was trying to filter out the . and .. directories incorrectly, which could have caused it to skip some files and to attempt to use DeleteFile on other directories. It's simpler (and easier-to-understand) to skip all directories.
Don't call CloseHandle on the handle returned by FindFirstFile.
Do call FindClose on the handle returned by FindFirstFile, but do so only in the case you got a valid handle from FindFirstFile.
As long as you're using Windows-specific APIs, you may as well use them consistently and not mix with library wrappers like rmdir. The library wrappers sometimes introduce surprising limitations or behavior, though I think rmdir would work correctly in this instance.
This still leaves a significant problem: It doesn't handle Unicode in the paths (and it requires you to compile for ANSI which limits Unicode handling in other parts of the project).

a better way than stat() to look for files in dir?

I'm trying to find out whether certain files are in a certain folder. However, even though the files exist, the way I try to find them doesn't work in certain folders.
bool FileExists(string strFilename) {
struct stat stFileInfo;
bool blnReturn;
int intStat;
intStat = stat(strFilename.c_str(),&stFileInfo);
if(intStat == 0) {
// We were able to get the file attributes
// so the file obviously exists.
blnReturn = true;
printf("Found file %s\n", strFilename.c_str());
} else {
blnReturn = false;
printf("Didn't find file %s\n", strFilename.c_str());
}
return(blnReturn);
}
When I mount a dir in /mnt/ram .. it doesn't (and sometimes does ) find the files there, however when I use another directory which is on the disk, it always finds the files.
Is there any other way to find out whether files exist in the directory?
Thanks
The result of a stat call or any other directory/file listing depends on permissions of the calling process. /mnt/ram might well be hidden for the current user.
As mentioned in the comments, opendir and readdir are the idiomatic way to get a (recursive) directory listing. Obviously, stat is part of the idiom :-).

How to know and load all images in a specific folder?

I have an application (C++ Builder 6.0) that needs to know the total of images there are in a specific folder, and then I have to load them: in an ImageList or in a ComboBoxEx... or any other control...
How can I do that?
I know how to load an image in a control, or to save in a TList, or in an ImageList... but How to know how many files files there are in the directory, and how to load every image in it??
I am Sorry about my English.
I did something like this yesterday with C++ using the boost::filesystem library. However, if you are not using boost already, I would strongly recommend you just use the windows libraries instead. This was my code though in case you're interested:
#include <algorithm>
#include <boost/filesystem.hpp>
#include <set>
namespace fs = boost::filesystem;
typedef std::vector<fs::path> PathVector;
std::auto_ptr<PathVector> ImagesInFolder(const fs::path& folderPath) {
std::set<std::string> targetExtensions;
targetExtensions.insert(".JPG");
targetExtensions.insert(".BMP");
targetExtensions.insert(".GIF");
targetExtensions.insert(".PNG");
std::auto_ptr<PathVector> paths(new PathVector());
fs::directory_iterator end;
for(fs::directory_iterator iter(folderPath); iter != end; ++iter) {
if(!fs::is_regular_file(iter->status())) { continue; }
std::string extension = iter->path().extension();
std::transform(extension.begin(), extension.end(), extension.begin(), ::toupper);
if(targetExtensions.find(extension) == targetExtensions.end()) { continue; }
paths->push_back(iter->path());
}
return paths;
}
This doesn't answer the part of your question about how to actually put the paths into a listbox though.
Use the Win32 functions FindFirstFile and FindNextFile ...?
There's no practical way to identify every image in an arbitrary folder. Almost anything you can't identify as something else, could be some sort of image. Then again, using steganography, even something you can identify as something else still might be (or contain) at least part of an image as well.
Realistically, you want to pick out a set of formats you want to support, and write code that knows about them. For quite a few purposes, a half dozen formats or so is quite adequate, though the exact half dozen you pick will vary by the type of application -- only a few programs have any use for both bitmapped and vector graphics, for one example.
Once you've decided what you want, DlgDirList is probably the easiest way to list some files. If that isn't flexible enough for your purposes, the next obvious choice is FindFirstFile, FindNextFile, and FindClose.
To get a list of all files in a folder, have a look at the FindFirst and FindNext functions in SysUtils.
Here is an example function which shows how to get a list of files.
void __fastcall TForm1::GetDirList(TStrings *List, const AnsiString SearchStr)
{
TSearchRec SRec;
AnsiString TempFName;
List->Clear();
// start search
if (FindFirst(SearchStr, faAnyFile, SRec) == 0)
{
do
{
if ((SRec.Attr & faDirectory) != faDirectory) // exclude directories
{
List->Add(SRec.Name);
} // end if
}
while (FindNext(SRec) == 0);
FindClose(SRec);
} // end if
}
Examples:
// get list of all files in directory
GetDirList(MyStringList, "C:\images*.*");
// get list of all .bmp files in directory
GetDirList(MyStringList, "C:\images\*.bmp");
If you can upgrade to newer version of C++Builder, have a look at TMS AdvSmoothImageListBox, from TMS Software.
The TMS Smooth Controls are available free for C++Builder 2010 users on the from Embarcadero website.