C++ code for copying FILES : : confused about relative address (tilde) - c++

I've written a simple program to copy files.
It gets two strings :
1) is for the path of the source file.
2) is for name of a copy file.
It works correctly when I give it the absolute or relative path(without tilde sign (~)).
But when I give it a relative path with tilde sign (~) it can't find the address of a file. And it makes me confused !
Here is my sample input :
1) /Users/mahan/Desktop/Copy.cpp
2) ~/Desktop/Copy.cpp
The first one works correctly but the second one no.
And here is my code :
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
string path, copy_name;
cin >> path >> copy_name;
ifstream my_file;
ofstream copy(copy_name);
my_file.open(path);
if(my_file.is_open())
{
copy << my_file.rdbuf();
copy.close();
my_file.close();
}
}

The ~ is handled by the shell you're using to auto expand to your $HOME directory.
std::ofstream doesn't handle the ~ character in the filepath, thus only your first sample works.
If you pass the filepath to your program from the command line using argv[1], and call it from your shell, you'll get the ~ automatically expanded.
With what was said above, if you want to expand the ~ character yourself, you can use the std::getenv() function to determine the value of $HOME, and replace it with that value.

The second example does not work because the shell is what replaces ~ with $HOME, i.e. the path to your home directory.
fstream objects will not perform this replacement and will instead look for a directory actually called ~, which likely does not exist in your working directory.

std::ofstream can't handle ~. It is a shortcut to your home directory. You need to give absolute path of home or the relative path with respect to the code run directory for it to work.
To give relative path, For example, if you are running your code in Desktop directory, then you needn't give ~/Desktop/Copy.cpp. Just give Copy.cpp and it should suffice.

Related

How to specify file storage

I work with C ++ and I am almost new.
My question is how to specify where to save the text file.
For example, I wrote a program that creates a text file called "usertext.txt"
And it automatically creates the text file in the code storage. But I want to create a folder For example called patch and save the text file there .
How can this be done?
#include <iostream>
#include <fstream>
using std::ios;
using std::fstream ;
int main(){
fstream filetext;
filetext.open("usertext.txt", ios::app) ;
filetext << "hellow world!" ;
filetext.close();
}
Use the functions in the filesystem library to create the directory first. Then use the directory as part of the full name when opening the file.
std::filesystem::create_directories ("/path/to/");
filetext.open("/path/to/usertext.txt", ios::app) ;
You can also use the path class to form the full name if the user gives the file name separately from the directory.
The function create_directories will create each part of the path that does not already exist; if the full thing already exists it does nothing.
You can just use the path to the folder
filetext.open("patch/usertext.txt", ios::app);
Note however that it won't handle the directory creation if it doesn't exist.
If you can use C++17, you can probably manage the path better using std::filesystem::path. But it might be overkill for your use case.

Open an ofstream with tilde (~) in path name [duplicate]

This question already has an answer here:
C++ paths beginning with ~ [duplicate]
(1 answer)
Closed 3 years ago.
I have to open some file for writing, and its name contains the tilde sign (~). The following code fails to create the desired text file. If I replace the ~ with /home/oren then everything works fine.
#include <fstream>
#include <string>
const std::string dirname = "/home/oren/GIT/";
// const std::string dirname = "~/GIT/";
const std::string filename = "someTextFile";
int main(int argc, char **argv)
{
std::ofstream log_file(dirname+filename+".txt");
log_file << "lorem ipsum";
log_file.close();
}
Is there any way to (easily) handle a file with ~ in its name?
The ~ shortcut in paths is not something magical at the filesystem level, opening ~/GIT literally tries to access ~/GIT, i.e: a file named GIT in the ~ directory. You can verify this by creating ~ and GIT first.
In the command line, ~ is typically resolved by your shell. e.g: in bash:
~ : The value of $HOME
https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html
Therefore, to achieve the same effect, you have to query the $HOME envvar, and replace the usage of leading ~/ in the path:
#include <stdlib.h>
const char* home = getenv("HOME")
if (home) { /* replace ~/ with home */ }
In addition, on linux, the wordexp function can be used to perform these replacements (~ to current user, ~other_user to home of other user)
The tilde is part of the shell expansion, it's not something handled by the underlying operating system. You need to resolve it yourself.
One simple way is to replace leading "~/" with the contents of the environment variable HOME (if it exists).
The tilde is expanded to the home directory by the shell. The iostreams don't use a shell, so you have to take care of the expansion for them. Tilde is a actually a valid character to use in a file name so without expansion, a file is created into a directory named ~ - which fails if the directory does not exist.
There is no standard way in C++ for shell expansions, nor a way to get the home directory, but there are several ways in POSIX systems:
wordexp is probably one of the most useful functions for this case. You can pass the path to the function and it will expand the tilde, as well as variables and braces. An example:
std::string full = dirname+filename+".txt"
wordexp_t p;
wordexp(full.c_str(), &p, 0);
std::string expanded = p.we_wordv[p.we_offs];
wordfree(&p);
std::ofstream log_file(expanded);
Other alternatives:
getpwuid gives you a structure with the home directory as a member. This can be used to get home directory of another user as well, in case that is needed.
HOME environment variable should also be available. It can be accessed with the standard std::getenv.

How do you create a folder in C++ with no path name just the name of the folder you want to create? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Creating a directory In C or C++
I want to make a folder that is titled "BobtheBuilder". And then I want to create a text file inside of it. I want to do this without being aware of my path. I don't want to have to type in:
ofstream out("C:/MyComputer/User/Jeff/etc/BobtheBuilder/NewFile.txt");
I want it just to be local to this area where my executable is contained like this:
ofstream out("/BobtheBuilder/NewFile.txt");
is this possible? Do I have to know the whole path name in order to do file management? I feel like this is possible because you can create or open a file that is in the same directory as the program like:
ifstream inf("NewFile.txt");
Or is there a special keyword that fills in the previous path like this:
ifstream inf("FILLIN/BobtheBuilder/NewFile.txt");
Thanks
You can absolutely specify a relative path like "BobtheBuilder/NewFile.txt" without specifying the whole path.
You would however need to create the folder first before the file.
Since creating folders is platform specific and since you're on Windows, you would need to call the CreateDirectory function with "BobtheBuilder" as its parameter.
The folder would then be created in the default working directory of the program which is the same folder where the executable resides.
You can change this working directory using the SetCurrentDirectory function before creating the folder and file.
For creating a directory you can use the C function:
int mkdir(const char *pathname, mode_t mode);
If you can use Boost, then it really becomes easier and more C++ friendly:
bool create_directories(const path& p);
// usage example
boost::filesystem::create_directories("./BobtheBuilder");
As you mention in your question , you can use both absolute and relative paths. It just depends on what is your intention. In your case, you could just do:
boost::filesystem::create_directories("./BobtheBuilder");
ofstream out("./BobtheBuilder/NewFile.txt");
not needing to specify the absolute path at all.
If you often need to manage paths, Boost provides many useful tools for path management. Just as an example, consider the problem you mention in your question: you want to get the full path to the current directory and then append a relative path. You could do this very easily:
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
...
fs::path curr_abs_path = fs::current_path();
fs::path rel_path = "foo/bar";
fs::path combined = (curr_abs_path /= rel_path);
cout << combined << endl;
Assuming the current directory is /tmp/ the previous code snippet would print:
/tmp/foo/bar
operator/= is responsible for appending two paths and returning the combined result.

How to use fstream objects with relative path?

Do I always have to specify absolute path for objects instantiated from std::fstream class? In other words, is there a way to specify just relative path to them such as project path?
You can use relative paths as well. But they are relative to the environment you call your executable from.
This is OS dependent but all the major systems behave more or less the same AFAIK.
Windows example:
// File structure:
c:\folder\myprogram.exe
c:\myfile.txt
// Calling command from folder
c:\folder > myprogram.exe
In the above example you could access myfile.txt with "c:/myfile.txt" or "../myfile.txt". If myprogram.exe was called from the root c:\ only the absolute path would work, but instead "myfile.txt" would work.
As Rob Kennedy said in the comments there's really nothing special about paths regarding fstream. But here is a code example using a relative path:
#include <fstream>
int main() {
std::ifstream ifs("../myfile.txt");
... // Do something sensible with the file
}
If you have an .exe file running from C:\Users\Me
and you want to write a file to C:\Users\Me\You\text.txt,
then all what you need to do is to add the current path operator ., so:
std::ifstream ifs(".\\you\\myfile.txt");
will work
You can use relative paths. They're treated the same as relative paths for any other file operations, like fopen; there's nothing special about fstream in that regard.
Exactly how they're treated is implementation-defined; they'll usually be interpretted relative to your process's current working directory, which is not necessarily the same as the directory your program's executable file lives in. Some operating systems might also provide a single working directory shared by all threads, so you might get unexpected results if a thread changes the working directory at the same time another thread tries to use a relative path.
Say you have a src folder directly under your project directory and the src folder contains another tmp_folder folder which contains a txt file named readMe.txt. So the txt file can be read in this way
std::ifstream fin("../src/tmp_folder/readMe.txt");
The behaviour is OS specific. Therefore, the best way to handle this IMHO is to make it somebody else's problem. Read the path to the file to open as a string from the user (e.g: command line argument, config file, env variable etc..) then pass that string directly to the constructor of fstream. Document that this is how your program behaves.
I wrote more about path manipulation here: https://stackoverflow.com/a/40980510/2345997
You can specify a path relative to current directory. On Windows you may call GetCurrentDirectory to retrieve current directory or call SetCurrentDirectory to set current directory. There are also some CRT functions available.
On linux also:
// main.cpp
int main() {
ifstream myFile("../Folder/readme.txt");
// ...
}
Assuming the folder structure is something like this:
/usr/Douments/dev/MyProject/main.cpp
/usr/Documents/dev/MyProject/Folder/readme.txt
What I ended up using was a relative path as identified on How to open a file with relative path in C++? which ended up being:
myFile.open("../Release/frequency.dat", ios::in);
*changing myFile to whatever your variable is.

My ifstream doesn't seem to be working

This is a main file that I am using to test methods before I implement them. I am trying to get the list of all files in a directory, write them to a txt file (It works fine until here), then read the file names from that text file.
using namespace std;
string sysCall = "", location = "~/Documents/filenames.txt";
string temp = "";
sysCall = "ls / > "+location;
system(sysCall.c_str());
ifstream allfiles(location.c_str());
allfiles.good();
getline(allfiles, temp);
cout<<temp<<endl; //At this point, the value of temp is equal to ""
return -1;
After the program runs, no text has been outputted. From what I've read in other peoples' questions, this should work (but obviously doesn't). What am I doing wrong here?
EDIT: allfiles.good() returns false, but I don't understand why it would return that...
ifstream allfiles("~/Documents/filenames.txt"); doesn't do what you think it does. The tilde ~ character is not part of the filename -- it is a special character interpreted by some shells. You need the entire path, with no ~ or $ characters in it.
Try setting location to "/tmp/filenames.txt", or just "filenames.txt".
Also, if Boost.Filesystem is available to you, you could use a directory_iterator instead of invoking /bin/ls.
I'll bet the system() call expands the ~ in the filename to your home directory (e.g. /home/mrswmmr), but ifstream does not. Replace the ~ with the full path to your home directory and it should work.
It has no guarantee to work because system gives no guarantee.