Read file names from a directory - c++

I was wondering if there's an easy way in C++ to read a number of file names from a folder containing many files. They are all bitmaps if anyone is wondering.
I don't know much about Windows programming so I was hoping it can be done using simple C++ methods.

Boost provides a basic_directory_iterator which provides a C++ standard conforming input iterator which accesses the contents of a directory. If you can use Boost, then this is at least cross-platform code.

C++17 includes a standard way of achieve that
http://en.cppreference.com/w/cpp/filesystem/directory_iterator
#include <fstream>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
fs::create_directories("sandbox/a/b");
std::ofstream("sandbox/file1.txt");
std::ofstream("sandbox/file2.txt");
for(auto& p: fs::directory_iterator("sandbox"))
std::cout << p << '\n';
fs::remove_all("sandbox");
}
Possible output:
sandbox/a
sandbox/file1.txt
sandbox/file2.txt

Since you specify Windows,
I think you're looking for FindFirstFile() and FindNextFile().

Just had a quick look in my snippets directory. Found this. This uses Microsoft's Win32 API directly:
vector<CStdString> filenames;
CStdString directoryPath("C:\\foo\\bar\\baz\\*");
WIN32_FIND_DATA FindFileData;
HANDLE hFind = FindFirstFile(directoryPath, &FindFileData);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
if (FindFileData.dwFileAttributes != FILE_ATTRIBUTE_DIRECTORY)
filenames.push_back(FindFileData.cFileName);
} while (FindNextFile(hFind, &FindFileData));
FindClose(hFind);
}
This gives you a vector with all filenames in a directory. It only works on Windows of course.
João Augusto noted in an answer:
Don't forget to check after FindClose(hFind) for:
DWORD dwError = GetLastError();
if (dwError != ERROR_NO_MORE_FILES)
{
// Error happened
}
It's especially important if scanning on a network.

You could also use the POSIX opendir() and readdir() functions. See this manual page which also has some great example code.

Since you are programming for Windows I recommend you to use the native Win32 FindFirstFile() and FindNextFile() functions. These give you full control over how you search for files. These are simple C APIs and are not hard to use.
Another advantage is that Win32 errors are not hidden or made harder to get at due to the C/C++ library layer.

Another alternative is -
system("dir | findstr \".bmp\" > temp.txt ");
Now read temp.txt line by line to get all filenames.

Why not use glob()?
glob_t glob_result;
glob("/foo/bar/*",GLOB_TILDE,NULL,&glob_result);
for(unsigned int i=0;i<glob_result.gl_pathc;++i){
cout << glob_result.gl_pathv[i] << endl;
}

Related

C++ Run a shell command on the same shell instance

So as the title says, I would like to run a shell command on the same shell process/instance in C++ and communicate with it, how can I do that?
I have looked up every corner and I couldn't find an appropriate/straight to the point answer.
I am not a C++ overlord, my answer could be dumb.
Now I could use a combination of fork/exec on unix, but I'm on windows.
If there is a cross platform solution, please mention it below.
Thanks in advance.
Pseudocode:
SHELL shell = make_shell();
shell.run("cd desktop");
shell.run("dir");
print(shell.stdout)
The standard library in C++ does not provide a way to do what you want. However, you can use a third party library, like boost::process, which will make the communication part between your program and the sub-processes pretty portable. If you want to execute built-in shell commands, or commands using the shell, you will still need to deal with differences in the shells used on the different platforms.
The boost::process library contains a lot of different ways of creating and communicating with sub processes (reference), both synchronously and asynchronously, so browse through the reference page to get a feeling for what it can do.
Here's a simple example that uses boost::process::system to run the command dir in the directory desktop and collects the output from that command. It does not use the shell (which should generally be avoided) but uses boost::process::search_path to find the command (in your PATH) and executes the command directly without involving a shell:
#include <boost/process.hpp>
#include <boost/process/start_dir.hpp>
#include <iostream>
#include <string>
int main() {
namespace bp = ::boost::process;
bp::ipstream out;
bp::ipstream err;
bp::system(bp::search_path("dir"),
bp::start_dir = "./desktop",
bp::std_out > out, // collect stdout in out
bp::std_err > err, // collect stderr in err
bp::std_in < stdin); // for commands reading from stdin
// display what was collected on stdout and stderr:
std::string line;
std::cout << "stdout capture:\n";
while(std::getline(out, line)) {
std::cout << '[' << line << "]\n";
}
std::cout << "\nstderr capture:\n";
while(std::getline(err, line)) {
std::cout << '[' << line << "]\n";
}
}
Note: You need to link with boost_filesystem and boost_atomic for this example to work.
Linux: exec
Windows: CreateProcessA
write ifdefs to switch based upon platform.
I found this answer: https://stackoverflow.com/a/57096619/9282847.
It seems to be the only method which is both standard, and portable (assuming the implementation supports std::filesystem).
Like:
#include <filesystem>
#include <iostream>
int main() {
namespace fs = std::filesystem;
const auto kDesktopPath = fs::path("~/Desktop"); // portable filepath
fs::current_path(kDesktopPath); // set CWD (This will throw on compiler explorer)
auto dir_it = fs::recursive_directory_iterator{ fs::current_path() };
for (auto& f: dir_it) {
std::cout.put('\n') << f;
}
}
Live example on compiler explorer

find file starting with a certain string [duplicate]

This question already has answers here:
Fastest way to check if a file exists using standard C++/C++11,14,17/C?
(23 answers)
Closed 7 years ago.
I wonder how to check If a file exist or not :
For Example I have many files :
Boba.txt
James.txt
Jamy.txt
Boby.txt
How can I check if the file starting with Bob exists ?
Note, I'm assuming that you're on a Windows system and that the files are in the same directory.
You can use the FindFirstFile and FindNextFile functions to iterate through a directory. The prefix can be included in search term.
Example
std::string strSearch = "C:\\Some Directory\\Bob*";
WIN32_FIND_DATAA ffd;
HANDLE hFind = FindFirstFileA(strSearch .c_str(), &ffd);
do
{
std::string strFile = ffd.cFileName;
}
while (FindNextFileA(hFind, &ffd) != 0);
Proper error checking and how to deal with directories is left as an exercise for the reader.
Assuming you need to do this within C++, the boost::filesystem library can be very helpful. In that case, your problem can be solved by a variant of the solution posted here. In your case, you don't need a std::multimap, but can just use simple std::string::find.
You could open a file with fopen as read only "r". fopen will return a NULL pointer if the file does not exist. You can do this for any of the files needed by your program.
#include <cstdio>
int main ()
{
FILE **pFile;
pFile[0] = fopen ("Boba.txt","r");
pFile[1] = fopen ("Boby.txt","r");
if (pFile[0]!=NULL || pFile[1]!=NULL){
printf("File starting with \"bob\" exists.");
}
else printf("File does not exist.");
return 0;
}

Listing Folders in a Directory C+

I am aware that this may well be a duplicate. However, I am struggling to actually get a working answer.
What I am trying to do is list all of the folders in the working directory. Below is some code that I have adapted from the MS website (http://msdn.microsoft.com/en-us/library/windows/desktop/aa365200(v=vs.85).aspx)
This gives the output:
Filname:52428
I have checked the folder - and there are three folders that I am wanting to list 'Vidoe' 'John' 'David' I am not sure as to why it is printing out the result above.
I do not want to use Boost - nor to download any third party plugings.
int main(int argc, char** argv)
{
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA ffd;
//The Directory where the .exe is run from.
hFind = FindFirstFile(TEXT(".\\Players\\*"), &ffd);
do
{
Sleep(1000);
bool isDirectory = ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
if(isDirectory)
{
cout << "DirectoryName: " << *ffd.cFileName << endl;
}
else
{
cout << "FileName: " << *ffd.cFileName << endl;
}
}while(FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
}
EDIT:
I do not have a specific way that I want to do this, all I am wanting to do is output the Folders in the directory - I do not care how it is done.
In …
*ffd.cFileName
remove the *.
Also remove the call to Sleep.
Also remove the silly TEXT macro call, use wide string literals like L"blah".
Oh I forgot, also replace the do loop with a while loop (or for loop), because it's not sure that the FindFirstFile call will succeed.
Oh, and important, for the debug output use wcout, not cout. The latter doesn't know anything about output of Unicode strings. But wcout can handle them.
The output you're getting,
52428
appears to be wchar_t value 0xCCCC, treated as an integer by cout, which value indicates uninitialized storage, which implies that the FindFirstFile call failed.
So, also be sure about the current directory when you run the program. A good idea is to run it from the command line. Then you're sure.

how to get system or user temp folder in unix and windows?

I am writing a C++ problem. It need to work on both Windows and Unix OS.
How to get user or system tmp folder on different OS?
Update: Thanks #RoiDanton, the most up to date answer is std::filesystem::temp_directory_path (C++17)
Try boost::filesystem's temp_directory_path() which internally uses:
ISO/IEC 9945 (POSIX): The path supplied by the first environment variable found in the list TMPDIR, TMP, TEMP, TEMPDIR. If none of these are found, "/tmp", or, if macro __ANDROID__ is defined, "/data/local/tmp"
Windows: The path reported by the Windows GetTempPath API function.
Interestingly, Window's GetTempPath uses similar logic to the POSIX version: the first environment variable in the list TMP, TEMP, USERPROFILE. If none of these are found, it returns the Windows directory.
The fact that these methods primarily rely on environment variables seems a bit yuck. But thats how it seems to be determined. Seeing as how mundane it really is, you could easily roll your own using cstdlib's getenv function, especially if you want specific order prioritization/requirements or dont want to use another library.
Use the $TMPDIR environment variable, according to POSIX.
char const *folder = getenv("TMPDIR");
if (folder == 0)
folder = "/tmp";
if you use QT(Core) you can try QString QDir::tempPath() , or use it's implementation in your code (QT is open, so, check how they do).
The doc say : On Unix/Linux systems this is usually /tmp; on Windows this is usually the path in the TEMP or TMP environment variable.
According to the docs, the max path is MAX_PATH (260). If the path happens to be 260, the code in the sample above (als plougy) will fail because 261 will be returned. Probably the buffer size should be MAX_PATH + 1.
TCHAR szPath[MAX_PATH + 1];
DWORD result = GetTempPath(MAX_PATH + 1, szPath);
if (result != ERROR_SUCCESS) {
// check GetLastError()
}
Handy function :
std::string getEnvVar( std::string const & key )
{
char * val = getenv( key.c_str() );
return val == NULL ? std::string("") : std::string(val);
}
I guess TEMP or something could be passed as an argument? Depending on the OS of course. getenv is part of stdlib so this should also be portable.
If you get an access to main() function code, may be better is to put necessary folder names through the main()'s **argv and use an OS-dependend batch launcher.
For example, for UNIX
bash a_launcher.sh
where a_launcher.sh is like
./a.out /tmp
On Windows: Use GetTempPath() to retrieve the path of the directory designated for temporary files.
wstring TempPath;
wchar_t wcharPath[MAX_PATH];
if (GetTempPathW(MAX_PATH, wcharPath))
TempPath = wcharPath;
None of these examples are really concrete and provide a working example (besides std::filesystem::temp_directory_path) rather they're referring you to microsoft's documentation, here's a working example using "GetTempPath()" (tested on windows 10):
//temp.cpp
#include <iostream>
#include <windows.h>
int main()
{
TCHAR path_buf[MAX_PATH];
DWORD ret_val = GetTempPath(MAX_PATH, path_buf);
if ( ret_val > MAX_PATH || (ret_val == 0) )
{
std::cout << "GetTempPath failed";
} else {
std::cout << path_buf;
}
}
outputs:
C:\>temp.exe
C:\Users\username\AppData\Local\Temp\

Example of using FindFirstFIleEx() with specific search criteria

I asked about finding in subdirs with criteria. First answer was use FindFirstFileEx(). It seems the function is no good for this purpose or I'm using it wrong.
So can someone explain how I would go about searching in a folder, and all it's subfolders for files that match (to give some sample criteria) .doc;.txt;*.wri; and are newer than 2009-01-01?
Please give a specific code example for those criteria so I know how to use it.
If it isn't possible, is there an alternative for doing this not-at-all-obscure task??? I am becoming quite baffled that so far there aren't well known/obvious tools/ways to do this.
From MSDN:
If you refer to the code fragment in that page:
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
void _tmain(int argc, TCHAR *argv[])
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
if( argc != 2 )
{
_tprintf(TEXT("Usage: %s [target_file]\n"), argv[0]);
return;
}
_tprintf (TEXT("Target file is %s\n"), argv[1]);
hFind = FindFirstFileEx(argv[1], FindExInfoStandard, &FindFileData,
FindExSearchNameMatch, NULL, 0);
if (hFind == INVALID_HANDLE_VALUE)
{
printf ("FindFirstFileEx failed (%d)\n", GetLastError());
return;
}
else
{
_tprintf (TEXT("The first file found is %s\n"),
FindFileData.cFileName);
FindClose(hFind);
}
}
You'll see that you can call FindFirstFileEx, where argv1 is a string (LPCSTR) pattern to look for, and &FindFileData is a data structure that contains file info of the found data.. hFind is the handle you use on subsequent calls with FindNextFile.. I think you can also add more search parameters by using the fourth and sixth parameter to FindFirstFileEx.
Good luck!
EDIT: BTW, I think you can check a file or dir's attributes by using GetFileAttributes() .. Just pass the filename found in FileFindData.. (filename can refer to a file's name or a directory name I think)
EDIT: MrVimes, here's what you could do (in pseudocode)
find the first file (match with *)
Check the file find data if it is ".", ".." (these are not really directories or files)
if check passed, check file find data if it has the attributes you are looking for (i.e. check filename, file attributes, even file creation time can be checked in the file find data, and what not) and do whatever with it
if check passed, do whatever you need to do with the file
if check failed, either call findnextfile or end, up to you
Something like that..
I think you use FindFirstFile to find all files and ignore the ones whose WIN32_FIND_DATA values don't match your search criteria.
Well you could use it to search for *.doc, *.txt and *.wri by passing those values as the name to search for:
FindFirstFileEx("*.doc", FindExInfoStandard, &fileData, FindExSearchNameMatch, NULL, 0);
To search by date is a little more complicated, but not overly so:
SYSTEMTIME createTime;
SYSTEMTIME searchDate;
FILETIME compareTime;
HANDLE searchHandle;
searchDate.wYear = 2009;
searchDate.wMonth= 1;
searchDate.wDay = 1;
SystemTimeToFileTime(searchDate, &compareTime);
searchHandle FindFirstFileEx("*", FindExInfoStandard, &fileData, FindExSearchNameMatch, NULL, 0);
if(searchHandle != INVALID_HANDLE_VALUE)
{
While(searchHandle != ERROR_NO_MORE_FILES)
{
FileTimeToSystemTime(fileData.ftCreationTime, &createTime);
if((ULARGE_INTEGER)compareTime < (ULARGE_INTEGER)createTime)
printf("%s matches date criteria", fileData.cFileName);
FindNextFile(searchHandle, &fileData);
}
}
You need to do two searches. The first is just to find the subdirs, and you do that without any file spec. The second search for the files uses the file spec.