This question already has answers here:
Checking if a dir. entry returned by readdir is a directory, link or file. dent->d_type isn't showing the type
(4 answers)
Closed 3 years ago.
I am trying to list all the files in a certain directory on a shared drive using the following code:
#include <iostream>
#include <string>
#include "dirent.h"
int main ()
{
DIR *directoryHandle = opendir("./temp/");
if (NULL != directoryHandle)
{
dirent *entry = readdir(directoryHandle);
while (NULL != entry)
{
//skip directories and select only files (hopefully)
if ((DT_DIR != entry->d_type) && (DT_REG == entry->d_type || DT_UNKNOWN == entry->d_type))
{
std::cout << "Name: " << entry->d_name << " Type:" << std::to_string(entry->d_type) << std::endl;
}
//go to next entry
entry = readdir(directoryHandle);
}
closedir(directoryHandle);
}
return 0;
}
The problem is that entry->d_type contains DT_UNKNOWN for directories as well as for files in the ./temp/ directory.
Is there any (reliable) linux-specific way to try and read each entry and determine if it's a file or directory?
The output of cat /etc/SuSE-release is:
SUSE Linux Enterprise Desktop 11 (x86_64) VERSION = 11 PATCHLEVEL = 1
The Linux version is: 2.6.32.59-0.7-default
Still, I expect this code to work on other platforms as well.
Use stat. You'll get a struct stat with an st_mode field, and macros S_ISDIR/S_ISREG:
isDirectory = S_ISDIR(statBuf.st_mode);
isFile = S_ISREG(statBuf.st_mode);
See man 2 stat. Include sys/stat.h.
If you are getting DT_UNKNOWN you're going to have to go ahead and call lstat() and inspect the st_mode field to determine if this is a file, directory, or symlink. If you don't care about symlinks, use stat() instead.
There's the boost filesystem library that has a command is_directory.
Using such a library would certainly make the code work on other platforms as well but I'm not sure if it will work for your specific problem.
Give this a try. It list files in a directory minus the folders :
#include <dirent.h>
#include <stdio.h>
# include <sys/types.h>
# include <sys/mode.h>
# include <stat.h>
DIR *d;
struct dirent *dir;
struct stat s;
d = opendir("./temp/");
if (d)
{
while ((dir = readdir(d)))
{
if (stat(dir->d_name,&s) != 0) {
/* is this a regular file? */
if ((s.st_mode & S_IFMT) == S_IFREG)
printf("%s\n", dir->d_name);
}
}
closedir(d);
}
unsigned char isFolder =0x4;
DIR Dir;
struct dirent *DirEntry;
Dir = opendir("c:/test/")
while(Dir=readdir(Dir))
{
cout << DirEntry->d_name;
if ( DirEntry->d_type == isFolder)
{
cout <<"Found a Directory : " << DirEntry->d_name << endl;
}
}
Related
I'm trying to code a program where it opens and reads a file automatically. But the problem is the file is stored in a folder which name is unknown. I only know where the folder is located and the file's name. How to get to that file's path in char* ?
Edit: example: d:\files\<random folder>\data.txt
I don't know the name of random folder but I know that it exists in d:\files
Since this is tagged windows, you might as well use the Windows API functions:
FindFirstFile()
FindNextFile()
to enumerate and loop through all the files in a given directory.
To check for a directory, look at dwFileAttributes contained in the WIN32_FIND_DATA structure (filled by the calls to Find...File()). But make sure to skip . and .. directories. If needed, this can be done recursively.
You can check the links for some examples, or see Listing the Files in a Directory.
In case you are using MFC, you can use CFileFind (which is a wrapper around the API functions):
CFileFind finder;
BOOL bWorking = finder.FindFile(_T("*.*"));
while (bWorking)
{
bWorking = finder.FindNextFile();
TRACE(_T("%s\n"), (LPCTSTR)finder.GetFileName());
}
Just for fun, I implemented this using the new, experimental <filesystem> FS Technical Specification supported by GCC 5.
#include <iostream>
#include <experimental/filesystem>
// for readability
namespace fs = std::experimental::filesystem;
int main(int, char* argv[])
{
if(!argv[1])
{
std::cerr << "require 2 parameters, search directory and filename\n";
return EXIT_FAILURE;
}
fs::path search_dir = argv[1];
if(!fs::is_directory(search_dir))
{
std::cerr << "First parameter must be a directory: " << search_dir << '\n';
return EXIT_FAILURE;
}
if(!argv[2])
{
std::cerr << "Expected filename to search for\n";
return EXIT_FAILURE;
}
// file to search for
fs::path file_name = argv[2];
const fs::directory_iterator dir_end; // directory end sentinel
// used to iterate through each subdirectory of search_dir
fs::directory_iterator dir_iter(search_dir);
for(; dir_iter != dir_end; ++dir_iter)
{
// skip non directories
if(!fs::is_directory(dir_iter->path()))
continue;
// check directory for file
// iterate through files in this subdirectory dir_iter->path()
auto file_iter = fs::directory_iterator(dir_iter->path());
for(; file_iter != dir_end; ++file_iter)
{
// ignore directories and wrong filenames
if(fs::is_directory(file_iter->path())
|| file_iter->path().filename() != file_name)
continue;
// Ok we found it (the first one)
std::cout << "path: " << file_iter->path().string() << '\n';
return EXIT_SUCCESS;
}
}
// Not found
std::cout << file_name << " was not found in " << search_dir.string() << '\n';
return EXIT_FAILURE;
}
The idea is: list the directories under d:\files and try to open
the file in each directory.
There isn't (yet) a standard C++ way of getting all the existing files/directories. A crude but easy way of doing this would be
system("dir d:\\files /b /ad > tmpfile");
This lists all directories (/ad), redirected to a temporary file. Then open the file:
std::ifstream list("tmpfile");
And read it:
std::string dirname;
std::string filename;
while (std::getline(list, dirname))
{
filename = "d:\\files\\" + dirname + "\\data.txt";
if ( ... file exists ... )
break;
}
I call this method crude because it has problems that are hard/impossible to fix:
It overwrites a potentially useful file
It doesn't work if current directory is read-only
It will only work in Windows
It might be possible to use _popen and fgets instead of redirecting to file.
So I'm looking for a piece of code that allows me to search for the path of the file it's being executed in. For example, I'm doing an autorun program for use in pendrives (example) but I don't know if it'll end up as D:, F:, G: or whatever so the program would search it's own path and open another file based on the path he is found using some 'if' statements.
Here's what I thought:
#include <stdlib.h>
#include <iostream>
using namespace std;
int main () {
// Insert 'search path' code and needed variables here.
if (-ThePath- == "d:\\AutoRun.exe")
{
system ("d:\\MyFolder\\OtherProgram.exe");
}
else if (-ThePath- == "f:\\AutoRun.exe")
{
system ("f:\\MyFolder\\OtherProgram.exe");
}
else if (-ThePath- == "g:\\AutoRun.exe")
{
system ("g:\\MyFolder\\OtherProgram.exe");
}
else
{
cout << "An error ocurred.\n";
cout << "Press enter to exit...\n";
cin.get();
};
return 0;
}
Is there some way this could be done?
GetModuleFileName : documentation here
EDITED - Pedro, the sample code from Microsoft handles a lot of things. To get the file path, all you need is :
TCHAR szPath[MAX_PATH];
if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ) {
// handle error in GetModuleFileName
} else {
// now, szPath contains file path
};
In standard C++ argv[0] contains the name of the executable. For a program invoked in the normal way this will be the path of the executable on Windows.
I am trying to create a program that deletes the contents of the /tmp folder, I am using C/C++ on linux.
system("exec rm -r /tmp")
deletes everything in the folder but it deletes the folder too which I dont want.
Is there any way to do this by some sort of bash script, called via system(); or is there a direct way i can do this in C/C++?
My question is similar to this one, but im not on OS X... how to delete all files in a folder, but not the folder itself?
#include <stdio.h>
#include <dirent.h>
int main()
{
// These are data types defined in the "dirent" header
DIR *theFolder = opendir("path/of/folder");
struct dirent *next_file;
char filepath[256];
while ( (next_file = readdir(theFolder)) != NULL )
{
// build the path for each file in the folder
sprintf(filepath, "%s/%s", "path/of/folder", next_file->d_name);
remove(filepath);
}
closedir(theFolder);
return 0;
}
You don't want to spawn a new shell via system() or something like that - that's a lot of overhead to do something very simple and it makes unnecessary assumptions (and dependencies) about what's available on the system.
In C/C++, you could do:
system("exec rm -r /tmp/*")
In Bash, you could do:
rm -r /tmp/*
This will delete everything inside /tmp, but not /tmp itself.
you can do
system("exec find /tmp -mindepth 1 -exec rm {} ';'");
by using use the wildcard * character you can delete all the files with any type of extension.
system("exec rm -r /tmp/*")
In C/C++ you can use (including hidden directories):
system("rm -r /tmp/* /tmp/.*");
system("find /tmp -mindepth 1 -delete");
But what if 'rm' or 'find' utilities are not availabe to sh?, better go 'ftw' and 'remove':
#define _XOPEN_SOURCE 500
#include <ftw.h>
static int remove_cb(const char *fpath, const struct stat *sb, int typeFlag, struct FTW *ftwbuf)
{
if (ftwbuf->level)
remove(fpath);
return 0;
}
int main(void)
{
nftw("./dir", remove_cb, 10, FTW_DEPTH);
return 0;
}
I realize this is very old question, but building on Demitri's great answer I created a function that will recursively delete files in subfolders if desired
It also does some error handling, in that it passes back errno. The function header is written for parsing by doxygen. This function works in the simple example cases I used, and deletes hidden folders and hidden files.
I hope this helps someone else in the future
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#define SUCCESS_STAT 0
/**
* checks if a specific directory exists
* #param dir_path the path to check
* #return if the path exists
*/
bool dirExists(std::string dir_path)
{
struct stat sb;
if (stat(dir_path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode))
return true;
else
return false;
}
/**
* deletes all the files in a folder (but not the folder itself). optionally
* this can traverse subfolders and delete all contents when recursive is true
* #param dirpath the directory to delete the contents of (can be full or
* relative path)
* #param recursive true = delete all files/folders in all subfolders
* false = delete only files in toplevel dir
* #return SUCCESS_STAT on success
* errno on failure, values can be from unlink or rmdir
* #note this does NOT delete the named directory, only its contents
*/
int DeleteFilesInDirectory(std::string dirpath, bool recursive)
{
if (dirpath.empty())
return SUCCESS_STAT;
DIR *theFolder = opendir(dirpath.c_str());
struct dirent *next_file;
char filepath[1024];
int ret_val;
if (theFolder == NULL)
return errno;
while ( (next_file = readdir(theFolder)) != NULL )
{
// build the path for each file in the folder
sprintf(filepath, "%s/%s", dirpath.c_str(), next_file->d_name);
//we don't want to process the pointer to "this" or "parent" directory
if ((strcmp(next_file->d_name,"..") == 0) ||
(strcmp(next_file->d_name,"." ) == 0) )
{
continue;
}
//dirExists will check if the "filepath" is a directory
if (dirExists(filepath))
{
if (!recursive)
//if we aren't recursively deleting in subfolders, skip this dir
continue;
ret_val = DeleteFilesInDirectory(filepath, recursive);
if (ret_val != SUCCESS_STAT)
{
closedir(theFolder);
return ret_val;
}
}
ret_val = remove(filepath);
//ENOENT occurs when i folder is empty, or is a dangling link, in
//which case we will say it was a success because the file is gone
if (ret_val != SUCCESS_STAT && ret_val != ENOENT)
{
closedir(theFolder);
return ret_val;
}
}
closedir(theFolder);
return SUCCESS_STAT;
}
You could use nftw(3). First, make a pass to collect the set of file paths to remove. Then use unlink (for non-directories) and rmdir(2) in a second pass
From C++17 onwards you can use std::filesystem. The code below will use directory_iterator to list all the files and subdirectories in a directory and call remove_all to delete them:
#include <filesystem>
namespace fs = std::filesystem;
void delete_dir_content(const fs::path& dir_path) {
for (auto& path: fs::directory_iterator(dir_path)) {
fs::remove_all(path);
}
}
Note that this will throw a filesystem_error exception on underlying OS API errors. You can avoid this with:
void delete_dir_content(const fs::path& dir_path) {
for (auto& path: fs::directory_iterator(dir_path)) {
std::error_code err;
std::uintmax_t n = fs::remove_all(path, err);
if (static_cast<std::uintmax_t>(-1) == n) {
std::cout << "Failed to remove_all(" << path << ") with error: " << err.message() << std::endl;
}
}
}
This question already has answers here:
Checking if a directory exists in Unix (system call)
(5 answers)
Closed 4 years ago.
How would I determine if a directory (not a file) existed using C++ in Linux? I tried using the stat() function but it returned positive when a file was found. I only want to find if the inputted string is a directory, not something else.
According to man(2) stat you can use the S_ISDIR macro on the st_mode field:
bool isdir = S_ISDIR(st.st_mode);
Side note, I would recommend using Boost and/or Qt4 to make cross-platform support easier if your software can be viable on other OSs.
how about something i found here
#include <dirent.h>
bool DirectoryExists( const char* pzPath )
{
if ( pzPath == NULL) return false;
DIR *pDir;
bool bExists = false;
pDir = opendir (pzPath);
if (pDir != NULL)
{
bExists = true;
(void) closedir (pDir);
}
return bExists;
}
Or using stat
struct stat st;
if(stat("/tmp",&st) == 0)
if(st.st_mode & S_IFDIR != 0)
printf(" /tmp is present\n");
If you can check out the boost filesystem library. It's a great way to deal with this kind of problems in a generic and portable manner.
In this case it would suffice to use:
#include "boost/filesystem.hpp"
using namespace boost::filesystem;
...
if ( !exists( "test/mydir" ) ) {bla bla}
The way I understand your question is this: you have a path, say, /foo/bar/baz (baz is a file) and you want to know whether /foo/bar exists. If so, the solution looks something like this (untested):
char *myDir = dirname(myPath);
struct stat myStat;
if ((stat(myDir, &myStat) == 0) && (((myStat.st_mode) & S_IFMT) == S_IFDIR)) {
// myDir exists and is a directory.
}
In C++17**, std::filesystem provides two variants to determine the existence of a path:
is_directory() determines, if a path is a directory and does exist in the actual filesystem
exists() just determines, if the path exists in the actual filesystem (not checking, if it is a directory)
Example (without error handling):
#include <iostream>
#include <filesystem> // C++17
//#include <experimental/filesystem> // C++14
namespace fs = std::filesystem;
//namespace fs = std::experimental::filesystem; // C++14
int main()
{
// Prepare.
const auto processWorkingDir = fs::current_path();
const auto existingDir = processWorkingDir / "existing/directory"; // Should exist in file system.
const auto notExistingDir = processWorkingDir / "fake/path";
const auto file = processWorkingDir / "file.ext"; // Should exist in file system.
// Test.
std::cout
<< "existing dir:\t" << fs::is_directory(existingDir) << "\n"
<< "fake dir:\t" << fs::is_directory(notExistingDir) << "\n"
<< "existing file:\t" << fs::is_directory(file) << "\n\n";
std::cout
<< "existing dir:\t" << fs::exists(existingDir) << "\n"
<< "fake dir:\t" << fs::exists(notExistingDir) << "\n"
<< "existing file:\t" << fs::exists(file);
}
Possible output:
existing dir: 1
fake dir: 0
existing file: 0
existing dir: 1
fake dir: 0
existing file: 1
**in C++14 std::experimental::filesystem is available
Both functions throw filesystem_error in case of errors. If you want to avoid catching exceptions, use the overloaded variants with std::error_code as second parameter.
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
bool isExistingDir(const fs::path& p) noexcept
{
try
{
return fs::is_directory(p);
}
catch (std::exception& e)
{
// Output the error message.
const auto theError = std::string{ e.what() };
std::cerr << theError;
return false;
}
}
bool isExistingDirEC(const fs::path& p) noexcept
{
std::error_code ec;
const auto isDir = fs::is_directory(p, ec);
if (ec)
{
// Output the error message.
const auto theError = ec.message();
std::cerr << theError;
return false;
}
else
{
return isDir;
}
}
int main()
{
const auto notExistingPath = fs::path{ "\xa0\xa1" };
isExistingDir(notExistingPath);
isExistingDirEC(notExistingPath);
}
If you want to find out whether a directory exists because you want to do something with it if it does (create a file/directory inside, scan its contents, etc) you should just go ahead and do whatever you want to do, then check whether it failed, and if so, report strerror(errno) to the user. This is a general principle of programming under Unix: don't try to figure out whether the thing you want to do will work. Attempt it, then see if it failed.
If you want to behave specially if whatever-it-was failed because a directory didn't exist (for instance, if you want to create a file and all necessary containing directories) you check for errno == ENOENT after open fails.
I see that one responder has recommended the use of boost::filesystem. I would like to endorse this recommendation, but sadly I cannot, because boost::filesystem is not header-only, and all of Boost's non-header-only modules have a horrible track record of causing mysterious breakage if you upgrade the shared library without recompiling the app, or even if you just didn't manage to compile your app with exactly the same flags used to compile the shared library. The maintenance grief is just not worth it.
i need a way to search the computer for files like Windows Explorer. i want my program to search lets say hard drive c:. i need it to search C:\ for folders and files (just the ones you could see in c:\ then if the user clicks on a file on the list like the folder test (C:\test) it would search test and let the user see what files/folders are in it.
Since you mentioned windows, the most straight forward winapi way to do it is with FindFirstFile and FindNextFile functions.
edit: Here's an example that shows you how to enumerate all files/folders in a directory.
#include <Windows.h>
#include <iostream>
int main()
{
WIN32_FIND_DATA file;
HANDLE search_handle=FindFirstFile(L"C:\\*",&file);
if (search_handle)
{
do
{
std::wcout << file.cFileName << std::endl;
}while(FindNextFile(search_handle,&file));
FindClose(search_handle);
}
}
This will be OS dependent. The SO question
How can I get a list of files in a directory using C or C++?
handles this problem well. You can download DIRENT here.
Now that you have this, I'd recommend recursively searching for a file with a DFS/BFS algorithm. You can assume the whole directory structure is a tree where each file is a leaf node and each subdirectory is an internal node.
So all you have to do is,
Get the list of files/folders in a directory with a function such as:
void getFilesFolders(vector<string> & dir_list, const string & folder_name)
If it's a directory, go to 1 with the directory name
If it's a file, terminate if it's the file you're looking for, else move on to the next file.
boost::filesystem can be a cross-platform solution for that (check out for such functions in it).
You can use Directory class members to do this with C# or managed C++. See the following MSDN article:
http://support.microsoft.com/kb/307009
If you wish to use C++ with MFC you can use CFileFind
http://msdn.microsoft.com/en-us/library/f33e1618%28v=VS.80%29.aspx
You'll have to supply your own browse window to present the file system tree.
Or you can use one of the directory/file controls to do both for you.
#include <Windows.h>
#include <iostream>
int FindF(char* pDirectory)
{
char szFindPath[MAX_PATH] = {0};
strcpy(szFindPath, pDirectory);
strcat(szFindPath, "\\*");
WIN32_FIND_DATA file;
HANDLE search_handle=FindFirstFile(szFindPath,&file);
if (search_handle)
{
do
{
if(file.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
{
strcpy(szFindPath, pDirectory);
strcat(szFindPath, "\\");
strcat(szFindPath, file.cFileName);
FindF(szFindPath);
}
std::wcout << file.cFileName << std::endl;
}while(FindNextFile(search_handle,&file));
CloseHandle(search_handle);
}
}
There really is no need to use 3rd party library to accomplish this. This is a short, independent function which lists all files (with their paths) in a directory, including subdiretories' files. std::string folderName has to finish with \, and if you want to list all files on computer, just create a loop in calling function along with GetLogicalDriveStrings (It returns strings with \, so it couldn't be more convenient in this case).
void FindAllFiles(std::string folderName)
{
WIN32_FIND_DATA FileData;
std::string folderNameWithSt = folderName + "*";
HANDLE FirstFile = FindFirstFile(folderNameWithSt.c_str(), &FileData);
if (FirstFile != INVALID_HANDLE_VALUE) {
do {
if (strcmp(FileData.cFileName, ".") != 0 && strcmp(FileData.cFileName, "..") != 0)
{
if(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
std::string NewPath = folderName + FileData.cFileName;
NewPath = NewPath + "\\";
FindAllFiles(NewPath);
}
else
{
std::cout /*<< folderName*/ << FileData.cFileName << std::endl;
}
}
} while(FindNextFile(FirstFile, &FileData));
}
}
This is ASCII version, remember that files and folders can be named in Unicode