Listing Folders in a Directory C+ - 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.

Related

C++ How do I delete files with _rmdir?

I made a simple program that deletes files, however, I don't know how to delete a directory. I saw a few posts saying I need to list all of the files in that directory, delete those files, and then delete the directory/folder itself. However, somebody suggested using _rmdir which as far as I understand, deletes a directory without any problems, however, it doesn't. Do I still need to list all of the files in a directory in order to delete it with _rmdir? Thanks!
Code:
#include <iostream>
#include <string>
#include <sys/stat.h>
#include <Windows.h>
#include <direct.h>
using namespace std;
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
inline bool fileExists(const string& filepath) {
struct stat buffer;
return (stat(filepath.c_str(), &buffer) == 0);
}
int main()
{
string filePath;
string fileAttribute;
cout << "[~] Enter a path to delete: ";
getline(cin, filePath);
cout << "\n[#] Checking if path exists..";
if (fileExists(filePath) == 1)
{
if (GetFileAttributes(filePath.c_str()) == FILE_ATTRIBUTE_DIRECTORY)
{
cout << "\n[!] Directory found!";
_rmdir(filePath.c_str());
cout << "\n[#] Deleting directory..";
}
else
{
cout << "\n[!] File found!";
remove(filePath.c_str());
cout << "\n[#] Deleting file..";
}
if (fileExists(filePath) == 0)
{
SetConsoleTextAttribute(h, 10);
cout << "\n[!] Deletetion successful!";
SetConsoleTextAttribute(h, 15);
}
else
{
SetConsoleTextAttribute(h, 12);
cout << "\n[!] Deletion unsuccessful!";
SetConsoleTextAttribute(h, 15);
}
}
}
You probably want the C++17 function std::filesystem::remove_all which deletes the contents of the directory and the contents of all its subdirectories, recursively, then deletes the directory itself as if by repeatedly applying the POSIX remove. Symlinks are not followed (symlink is removed, not its target).
Edit: As Eryk Sun pointed out in the comments, it's good to test that the recursive removal does not follow Windows junctions, which would lead to unwanted removal of everything the junction points at.
I made a test that used std::filesystem::create_directory_symlink to create a symlink and then use the WinAPI to check if that symlink became a junction, which it reportedly did - a junction has the FILE_ATTRIBUTE_REPARSE_POINT attribute and the reparse point tag IO_REPARSE_TAG_MOUNT_POINT. Interestingly it also had the IO_REPARSE_TAG_SYMLINK tag. When using normal DOS commands I noticed that the link was indeed reported as a plain <SYMLINKD> and not a <JUNCTION>.
I then used MKLINK /J to create a junction. That junction was indeed reported as a <JUNCTION> by DOS and running std::filesystem::remove_all() on it removed it, but not what it pointed at.
I also downloaded the sysinternals Junction utility and used that to create a junction.
That junction was also reported as a <JUNCTION> by DOS, but running std::filesystem::remove_all() on it had another effect. It did not remove it (or what it pointed at).
So remove_all() should be safe with regards to junctions - but perhaps even too safe. I didn't try to find out what the difference was between the MKLINK junction and the junction created by the sysinternals utility.
Do I still need to list all of the files in a directory in order to delete it with _rmdir?
Why don't you simply read the documentation?
It says:
The _rmdir function deletes the directory specified by dirname. The directory must be empty, and it must not be the current working directory or the root directory.
That's pretty much the case for any "remove directory" functionality. It's what your Windows Explorer UI is doing behind the scenes when you hit delete on your keyboard.
This is also the case for std::filesystem::remove, but std::filesystem::remove_all will do all that recursion for you.
You can use:
std::filesystem::remove_all("path/myDirectory");

How to fix CopyFile() error 5 - access denied error

I am trying to write a copy file function that can be used on both Linux and Windows. It works on Linux, but on Windows, I get error code 5 when trying to use the WinApi function CopyFile().
In header File.h
This is the custom defined function in the File namespace that I should be able to use on both Linux and windows.
class File
{
public:
static bool copyFile(std::string source, std::string destination);
private:
}
In File.cpp
For Linux it is simple:
#ifdef __unix__
#include "File.h"
bool File::copyFile(std::string source, std::string destination)
{
std::string arg = source + " " + destination;
return launchProcess("cp", arg);
}
#endif
In the Windows specific block of code, I use the WinAPI (#include < windows.h >) function CopyFile(). This accepts LPCWSTR data types instead of strings. To overcome this I have created a function that converts strings to LPCWSTR types.
#ifdef _WIN32
#include "File.h"
#include <Windows.h>
std::wstring strtowstr(const std::string &str)
{
// Convert an ASCII string to a Unicode String
std::wstring wstrTo;
wchar_t *wszTo = new wchar_t[str.length() + 1];
wszTo[str.size()] = L'\0';
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, wszTo,(int)str.length());
wstrTo = wszTo;
delete[] wszTo;
return wstrTo;
}
bool File::copyFile(std::string source, std::string destination)
{
std::wstring wsource = strtowstr(source);
std::wstring wdestination = strtowstr(destination);
int result = CopyFileW(wsource.c_str(), wdestination.c_str(), TRUE);
//for debugging...
std::wcout << "The error is " << GetLastError() <<std::endl;
std::wcout << wsource.c_str() << std::endl;
std::wcout << wdestination.c_str() << std::endl;
if (result == 0)
{
return false;
}
return true;
}
#endif
In my Test Programme
TEST(all,main_copy_file)
{
std::cout << "Testing copyFile() function..." << std::endl;
std::string srcDir = File::currentWorkingDirectory() + "srcDir";
File::makeDirectory(srcDir);
std::string destDir = File::currentWorkingDirectory() + "destDir/";
File::makeDirectory(destDir);
File::makeFile(srcDir, "testFile", ".txt");
ASSERT_TRUE(File::fileExists(srcDir + "/testFile.txt")) << "Error: Test file has not been generated" << std::endl;
ASSERT_TRUE(File::directoryExists(destDir)) << "Error: Destination directory does not exist" <<std::endl;
ASSERT_TRUE(File::copyFile(srcDir + "/testFile.txt", destDir)) << "Error: Coppy unsucsessfull" << std::endl;
ASSERT_TRUE(File::fileExists(destDir + "/testFile.txt")) << "Error: CoppyFile() flagged as sucsessfull but file does not exist" << std::endl;
}
In the application Output (on Windows)
/*
Testing copyFile() function...
The error is 5
C:\GIT\CorteX\Externals\OSAL\build\Debug/srcDir/testFile.txt
C:\GIT\CorteX\Externals\OSAL\build\Debug/destDir/
error: Value of: File::copyFile(srcDir + "/testFile.txt", destDir)
Actual: false
Expected: true
Error: Coppy unsucsessfull
*/
Error code 5 is an access denied error. I think it gives this error when either the directory does not exist, the directory is open somewhere else, or I do not have permissions.
Since I have tested that the directory does exist, I think it must be one of the latter two. I might only have restricted Admin rights (I don't know), but I can paste into the "destDir" without admin permission. So maybe it thinks the directory is open? Is there a command that exists to make sure the directory is closed?
The test is successful when running on Linux.
The CopyFile API expects file names for both source and destination files. Your code passes a directory name for the destination. This causes the API to fail. You need to append the file name for the destination as well.
Besides that, there are several other issues with your code:
The path separator on Windows is a backslash (\). Your are mixing forward slashes (/) and backslashes. Depending on the arguments passed, the system won't translate forward slashes to backslashes, before passing them on to lower-level file I/O API's.
You are calling GetLastError too late. You need to call it immediately, whenever it is documented to return a meaningful value. Do not intersperse it with any other code, however trivial it may appear to you. That code can modify and invalidate the calling thread's last error code.
Your code assumes ASCII-encoded strings. This will stop working, when dealing with files containing non-ASCII characters. This is quite common.
new wchar_t[...] buys you nothing over std::vector<wchar_t>, except the possibility to introduce bugs.
Your MultiByteToWideChar-based string conversion implementation makes (undue) assumptions about the code unit requirements of different character encodings. Those assumptions may not be true. Have the API calculate and tell you the destination buffer size, by passing 0 for cchWideChar.
Your string conversion routine ignores all return values, making bugs ever so likely, and unnecessarily hard to diagnose.
I know this is an old post, but for anyone who stumbles here needing more help:
CopyFile has the following constraints which if not met can give access denied error:
Insufficient permissions for the current user
File is in use
Filepath is a directory and not a file
File is read-only
In my case all the above were met, still I kept getting the same error. What helped me was a simple
SetFileAttributes(filePath,FILE_ATTRIBUTE_NORMAL)
Retrieving and Changing File Attributes
SetFileAttributes

C++ Read file with extended ASCII

I have a text file that contains a path. The path can be anything and can contain many different exotic characters. For the sake of example let's say it contains this:
C:\России общий приговор\file.dat
Now I'm using wifstream to fetch the line...
wifstream myfile(path_to_txt_file);
wstring path = L"";
if (!getline(myfile, path)) cout << "Couldn't open file!" << endl;
The problem arises when I want to check if the file in the path variable exists (I'm using Boost, but it doesn't really matter), it returns 0 even though it is present.
cout << boost::filesystem::exists(path) << endl; // 0
cout << boost::filesystem::exists(L"C:\\России общий приговор\\file.dat") << endl; // 1
Passing the variable in there returns 0, but hard-coding the path works, what's going on?
Do I need to set some locale settings? If so, how do I get this to work for everyone? Russian, Chinese, Swedish etc.

c++, output of FindFirstFile()

I wrote the program of finding file using FindFirstFile(...) function. But when I try to print the output of this function, there is a several strings of unknown characters printed in console windows.
I read some posts, there was written to try using wcout instead of cout. I try it, but it doesn't help. I think, that the problem is in difference between ANSI and UNICODE encodings. Can somebody help me? I will be very thankful for any help!
Here is my code:
#include "FindFile.h"
#include <iostream>
using namespace std;
void FindFileCl::Execute(Input * input, Response * response )
{
WIN32_FIND_DATAA FindFileData;
HANDLE h = FindFirstFileA((input->FileName).c_str(), // name of the file
&FindFileData);
if (h)
{
cout << "Search Results:\n";
cout<<(FindFileData.cFileName);
CloseHandle(h);
}
else
{
cerr << "File is NOT found:" << GetLastError() << "\n";
}
}
If FindFirstFile() fails it returns INVALID_HANDLE_VALUE, not NULL:
If the function fails or fails to locate files from the search string in the lpFileName parameter, the return value is INVALID_HANDLE_VALUE and the contents of lpFindFileData are indeterminate. To get extended error information, call the GetLastError function.
and INVALID_HANDLE_VALUE is #defined as -1 (following macro located in WinBase.h):
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
Meaning the if (h) will be entered in either success or failure. In the event of failure, cFileName will not be modified resulting in junk being printed as it is not initialized. Change the if condition to explicitly check for INVALID_HANDLE_VALUE:
if (h != INVALID_HANDLE_VALUE)
{
}
One of the "least bad" ways would be to convert the Unicode name to the Console's encoding.
For this, I suggest compiling in Unicode (There's a project option for that in Visual Studio >=8; otherwise you have to define both UNICODE and _UNICODE manually), using the TCHAR version of FindFirstFile(), and then using CharToOem() or CharToOemBuff() (neither is perfect). -- Or alternately, use the W version followed by WideCharToMultiByte(CP_OEMCP).

c++ list all directories and subdirectories within in (LINUX ) [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I am new in c ++ . could someone please give me some code of how to get all directories and all it's subdirectories RECURSIVELY in LINUX. i haven't found anything on internet that could help me ( or code that works.) I need to get all files withing folder and it;s subfolder.
IN UBUNTU I don't have getfiles, directories...
Try this on Linux:
#include <iostream>
#include <string>
#include <dirent.h>
void ProcessDirectory(std::string directory);
void ProcessFile(std::string file);
void ProcessEntity(struct dirent* entity);
std::string path = "/path/to/directory/";
int main()
{
std::string directory = "theDirectoryYouWant";
ProcessDirectory(directory);
return 0;
}
void ProcessDirectory(std::string directory)
{
std::string dirToOpen = path + directory;
auto dir = opendir(dirToOpen.c_str());
//set the new path for the content of the directory
path = dirToOpen + "/";
std::cout << "Process directory: " << dirToOpen.c_str() << std::endl;
if(NULL == dir)
{
std::cout << "could not open directory: " << dirToOpen.c_str() << std::endl;
return;
}
auto entity = readdir(dir);
while(entity != NULL)
{
ProcessEntity(entity);
entity = readdir(dir);
}
//we finished with the directory so remove it from the path
path.resize(path.length() - 1 - directory.length());
closedir(dir);
}
void ProcessEntity(struct dirent* entity)
{
//find entity type
if(entity->d_type == DT_DIR)
{//it's an direcotry
//don't process the '..' and the '.' directories
if(entity->d_name[0] == '.')
{
return;
}
//it's an directory so process it
ProcessDirectory(std::string(entity->d_name));
return;
}
if(entity->d_type == DT_REG)
{//regular file
ProcessFile(std::string(entity->d_name));
return;
}
//there are some other types
//read here http://linux.die.net/man/3/readdir
std::cout << "Not a file or directory: " << entity->d_name << std::endl;
}
void ProcessFile(std::string file)
{
std::cout << "Process file : " << fileToOpen.c_str() << std::endl;
//if you want to do something with the file add your code here
}
Recursion is unnecessary. There is a facility on Linux to do this iteratively.
#include <ftw.h>
int ftw(const char *dirpath,
int (*fn) (const char *fpath, const struct stat *sb,
int typeflag),
int nopenfd);
The ftw() function calls the supplied callback for every file and directory in the given tree.
Windows: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365200(v=vs.85).aspx
Unix/Linux: http://www.linuxquestions.org/questions/programming-9/c-list-files-in-directory-379323
Recursively apply the same algorithm you applied to the top-level directory.
In addition of Dmitri's answer, you might be interested in using the nftw library function which "does the recursion for you"
Use nftw. It provides various options to fine-tune the directory traversal.
That page also shows an example.
The answers on listing directories are only the first part of the answer but i didnt see anyone answering the recursive part. To recursively do anything you have to create a subroutine that "calls itself" -- note that you should take care when dealing with symbolic links especially in cases like /exports when using nfs, that could lead to circular recursion and lock you in an infinte loop! Basically:
this isn't real code, its pseudo-code to try to help you get a better idea
of how the recursion works without confusing you with language this idea can
be applied in any language that has some sort of call-return mechanism which
these days is just about every language i can think of
// PSEUDO-CODE
stiring entriesarray[] myfunc(string basedir)
{
string results[] = getfilesandfolders(basedir) // you might want to specify a filter
for each string entry in results
{
// if you want only files you'll need to test for if entry is a file
entriesarray.add(entry)
if (entry is a directory && entries is not a symbolic link)
{
string moreentriesarray[] = myfunc(entry)
entriesarray.join(moreentriesarray)
}
}
return entriesarray[]
}
notice how if the entries do not contain any real directories, the function doesn't call itself? this is important because this is how infinite recursion is avoided. Be warned however you might want to make it so this operation can be cancelled, larger filesystems
these days can take a lot of time to process. The way I usually do it is to start another
thread and do the search in the background, and have the background thread check for a
cancellation flag in case the user wants to stop the operation, and so it can post information about how much time is left, percentage complete, etc ,etc. its a bit crude but
this should get anyone interested in this type of thing going in the right direction. And
remember to ALWAYS properly check for errors and place exception handling, this is something
that I see new programmers skip ALL the time.