C++ How do I delete files with _rmdir? - c++

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");

Related

std::ofstream does not show error when permission denied C++

The following code when path = "c:\" doesn't write to file c:\err.txt because permission is denied. But it doesn't generate an error at the same time. Rather, it outputs "OK".
How I can check whether the permissions would allow the write?
#include <cstdlib>
#include <iostream>
#include <fstream>
using namespace std;
bool writeLineToErr(string path, string err_line){
std::ofstream outfile(path+"err.txt", std::ios_base::app);
if(!outfile){
cout<<"Error 1 "+path+"err.txt"+" can't open file!";
return false;
}
if(outfile.fail()){
cout<<"Error 2 "+path+"err.txt"+" can't open file!";
return false;
}
outfile << err_line << endl;
cout<<"OK";
outfile.close();
return true;
}
int main(int argc, char** argv) {
writeLineToErr("c:\\","Some Line");
return 0;
}
I'd say your code works and the write operation is actually done, but for the sake of it, add a check after the write too:
outfile << err_line << endl;
if(outfile.fail()) cout << "Error3\n";
else cout<<"OK";
On my system, I'll get your Error 1 ... can't open file if the file isn't opened for writing successfully.
Edit: Or are you running Windows with Compatibility Files virtualization still active? If so, the file will probably be in the Virtual Store, not in the real C:\err.txt path.
Example: C:\Users\username\AppData\Local\VirtualStore
If you find it there, you may find a lot of other stuff in there too. At least I did years ago when I had a similar problem. I decided to manually move (with admin rights) the few important files that some of my older programs had put there and then turn Virtual Store off. I can't find a good and simple official Microsoft link for how to turn off file and registry virtualization right now so perhaps this will do:
RegEdit:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\
Create a DWORD Key with the name EnableVirtualization and give it the value 0. If the key is already there, but set to something else than zero, change it.
There's more here:
UAC Group Policy Settings and Registry Key Settings

How to move file in Linux using C++

How do you move a file from one location to another using a C++ program in Linux?
I've written a program to do this and it runs, but when I try to move a file to a different directory it doesn't move the file, I get the error message from the cout statement. When I try to just rename the file, moving it to the same directory with a new name it works. How can I fix my code so it will be able to move files to another directory?
Here's the code I've written:
#include <iostream>
#include <stdio.h>
using namespace std;
int main ()
{
int result=1;
char oldname[500];
char newname[500];
cout << "Enter the name of a file you want to move (include directory structure)";
cin >> oldname;
cout << "Enter the new location (include directory structure)";
cin >> newname;
result = rename( oldname , newname );
if ( result == 0 )
cout << "File successfully moved" << endl;
else
cout << "Error moving file" << endl;
return 0;
}
Edit:
I added perror to my code and the error message displayed is "Error moving file: No such file or directory" even though the directory I tried moving it to does exist and it has create and delete files permissions.
Your code will work in most cases. But you are ignoring some important things in which case it will break :
The obvious things like permissions, non-existing path, ...
Paths of 500 chars or more. Don't use static allocated memory for oldname and newname
Moving between filesystems is not possible with rename() so do it like this ( and include iostream )
ifstream ifs(oldname, ios::in | ios::binary);
ofstream ofs(newname, ios::out | ios::binary);
ofs << ifs.rdbuf();
remove(oldname);
Before the remove() your total disk space will be a bit less.
This doesn't matter if your are moving between filesystems because only the free space on the filesystem with newname will shrink and this is free space you have because otherwise you wouldn't able to move the file here
If oldname and newname are on the same filesystem and you really care about this temporary loss then check whether you'll be using the same filesystem and use rename() after all.
How to fix your program depends on the reason why the move (rename) failed.
The reason for the failure can be found using errno.
In this case, it was necessary to make sure that the source file exists.
For all things that need to be considered to robustly implement moving, I recommend studying an implementation of mv command.

Somthing of bad understanding in "SetCurrentDirectory" ("windows.h") in CPP

I don't know what's happened here. Here is the code sample:
#include<stdio.h>
#include<iostream>
#include<string>
#include <Windows.h>
using namespace std;
int main()
{
char my_current_path[1024];
string current_path(R"(C:)");
if (!SetCurrentDirectory(current_path.c_str()))
cout << "cant change to that directory.";
GetCurrentDirectory(1024, my_current_path);
std::cout << my_current_path << endl;
system("pause");
return 1;
}
What I'm doing here is trying to change the directory to some directories.
There is two strange things with my code.
(1 Strange thing)
When I try to change to "c:" like that:
#include<stdio.h>
#include<iostream>
#include<string>
#include <Windows.h>
using namespace std;
int main()
{
char my_current_path[1024];
string current_path(R"(C:)");
if (!SetCurrentDirectory(current_path.c_str()))
cout << "cant change to that directory.";
GetCurrentDirectory(1024, my_current_path);
std::cout << my_current_path << endl;
system("pause");
return 1;
}
It's not working and not changing the path (and its the good thing.) but its not show me the message that shown when the directory isn't changed:
cout << "cant change to that directory.";
Why is that? (When I try to change something like "efef:" or "exist?" it is showing me that's messege. But Why here it doesn't show me, and also doest change the current working directory?
(Second Strange thing)
When I change the directory to "G:" Its for some reason working..
#include<stdio.h>
#include<iostream>
#include<string>
#include <Windows.h>
using namespace std;
int main()
{
char my_current_path[1024];
string current_path(R"(G:)");
if (!SetCurrentDirectory(current_path.c_str()))
cout << "cant change to that directory.";
GetCurrentDirectory(1024, my_current_path);
std::cout << my_current_path << endl;
system("pause");
return 1;
}
That code compiled, showing that I successfully changed the directory to "G:".
After changing to that path, I'm trying to change to "C:" (Which before, didn't did anything) and now its working! but in strange way, its not moving to "C:\" But to:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE
And its weird that before its not worked, and now when I'm in "G:" path and trying to move to "c:" again, then its move to path that I didn't even wanted!
If you want to specify the root directory on a drive, you must include a backslash, i.e., C:\.
The syntax you are using, C:, has a different meaning. The command shell separates the concepts of the current drive and the current directory on each drive, so that you can switch between drives quickly without having to retype the full path each time. This feature was introduced (a long time ago) for backwards compatibility reasons (see below) but is actually very useful; power users will sometimes map multiple drive letters to the same drive or network share specifically in order to take advantage of this functionality.
Although only the command shell keeps track of the current directory for each drive, the Win32 API knows how to look them up, and does so whenever you specify a drive but no path. As a special case, if the drive specified is the current drive, it expands to the actual current directory rather than the saved current directory.
So in your example the incomplete path C: is expanded to the current directory for the C drive; since your current directory is already on the C drive, SetCurrentDirectory(R"(C:)") is a no-op. If you want to change to the root directory, you should use SetCurrentDirectory(R"(C:\)") instead.
In your testing, there was no saved current directory for the G: drive, and it was not the current drive, so the effect was to set the current directory to G:\, i.e., you got the behaviour you expected but not for the reason you were expecting. If you had launched the program from the command shell and a current directory had been saved for the G drive, you'd have wound up there instead.
The same thing applies to opening files; C:file.txt will open file.txt from the current directory for the C drive, whereas C:\file.txt will open file.txt from the root directory.
See Raymond Chen's blog post, Why does each drive have its own current directory? for a discussion of the history of this feature.
you can use the two APIs for retrieving and setting the current directory: GetCurrentDirectory() and SetCurrentDirectory() only provide a valid path for the latter:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
char czCurDir[MAX_PATH] = "";
char czNewDir[MAX_PATH] = "";
// retrieve the current directory
GetCurrentDirectory(MAX_PATH, czCurDir);
cout << "Current directory: " << czCurDir << endl;// C:\Users\Raindrop7\Desktop\New folder
// set the value for the new directory NB: C:\\ not C:\ because the first backslah is an escape character
strcpy(czNewDir, "C:\\");
// set the current directory to "C:\"
SetCurrentDirectory(czNewDir);
// retrieve the current directory
GetCurrentDirectory(MAX_PATH, czCurDir);
cout << "current directory: " << czCurDir << endl;// C:\
return 0;
}
First change drive to single F or G drive-
Example::
if( !SetCurrentDirectory("F:") )
{
cout <<"can't open the directory"<< endl;
}
then give full path-
Example::
if(!SetCurrentDirectory("F://Python_Django_Webdevelopment//StudentManagement"))
{
cout <<"can't open the directory"<< endl;
}
It works for me.

unable to open files c++

I'm really struggling at the moment, originally had other issues with eclipse itself, that seems to have been resolved. Code looks right to me (compared to example code for loading files) however I'm not able to load anything as the error I put in is always triggered. No building errors atm. What am I doing wrong? Tried with both eclipse (mac) and Code::blocks (win vm), both seem to be having issues. the data files themselves are in the same folder as the .cpp file.
#include <iostream>
#include <string>
#include <math.h>
#include <fstream>
using namespace std;
int main() {
cout << "Choose which data file to load (1-4)" << endl;
int file;
cin >> file;
ifstream data;
switch (file) {
case 1:
data.open("dataSet1.txt");
case 2:
data.open("dataSet2.txt");
case 3:
data.open("dataSet3.txt");
case 4:
data.open("dataSet4.txt");
}
if (!data) {
cerr << "File not Loaded" << endl;
return -1;
}
string FullData[61];
for (int i=0; i=60; i++){
data >> FullData[i];
cout << FullData[i] << endl;
}
return 0;
}
EDIT: Got the program to stop showing the error, and it seems to be loading the files, however my assign/display loop doesn't seem to be working now as it displays only the last data point over and over again.
the data files themselves are in the same folder as the .cpp file
Being in the same folder as the .cpp is not important, the dataset files should be in the same folder as the compiled binary program.
It can also be that there is a working directory setting that does not point on the directory where your dataset files are. All that is being passed into the open member function is a string which means that interpreting what that string means depends on the environment settings.
Same issue. In my case as #Gluk36 pointed, the problem was the working directory settings.
In that case, you must deselect "Use default settings" and set where the binary is. I attach you a screenshot for your reference from eclipse CDT 4.9 under linux.
You must have break statement after each case
like:
case1://something;
break;
and you must close the stream with close() function
data.close();

boost filesystem copy_file "successful" but no files copied

im having trouble figuring out why my files wont copy. Here's a brief portion of the code:
(dir_itr is directory_iterator & root is a path)
if (!(is_directory(dir_itr->path())))
{
cout << "copying: " << dir_itr->path().filename() << endl;
try
{
copy(dir_itr->path(), root);
remove(dir_itr->path());
} catch (filesystem_error& ex) {
//more code
The results are as follows in the command window:
boost::filesystem::copy_file: The operation completed successfully:
"C:\Documents and Settings\R\Desktop\New Folder\New Folder (2)\New Bitmap Image 3.bmp",
"C:\Documents and Settings\R\Desktop\New Folder"
However no files are copied over.
I am basically just trying to move said file from folder c:\x\y\file.file to c:\x
I'm assuming why i cant move it is because i need a full file name and not just a directory or something? If this is the case, how do i convert path root to string so i can add a file name to it? (im gettin a thousand errors if i even try, they're so long i cant scroll all the way back up the window to see where it starts)
Perhaps boost::filesystem::system_complete can help:
(Sorry, I'm on my Mac and not windows but it shows a way to get the absolute path from a relative path). Good luck.
#include <iostream>
#include <boost/filesystem.hpp>
using namespace std;
int main(int argc, char *argv[]) {
boost::filesystem::path cwd(".");
boost::filesystem::path resolved = boost::filesystem::system_complete(cwd);
std::cout << cwd << std::endl;
std::cout << resolved << std::endl;
}
Outputs:
"."
"/private/var/folders/qw/x23nm9f11fxc45rgddb04n_w0000gn/T/CodeRunner/."
Got back to working on this and I added/changed the following:
try
{
string temp = root.string() + "\\" + dir_itr->path().filename().string();
path p(temp);
copy(dir_itr->path(), p);
remove(dir_itr->path());
//more code
And it seemed to work. I guess my assumption of needing to include the file name when copying was correct.