Unable to open file without it's full path in C++ - c++

I want to get file's content and print it in console, but the code bellow works only if I use full path and I don't want that, I am using Visual Studio 2022
int main() {
std::ifstream file("folder/file.txt");
std::string content = "";
if (file.is_open()) {
while (file) {
std::string line;
std::getline(file, line);
content += '\n' + line + '\n';
}
}
else {
std::cout << "Failed to open file" << std::endl;
}
std::cout << content << std::endl;
return 0;
}
output: Failed to open file
but if I use full path like: "C:\Users\User\...\folder\file.txt" it will work.

Some of the shorter comments give clues to the problem, but I'll provide a longer answer.
Many IDEs -- including Visual Studio -- do NOT have the current directory as your project area. Instead, the current directory when you run your program is in a build area.
Imagine your project is in C:\Users\You\MyProgram. You put the file there (or in a subdirectory), and when you run your program from the IDE, you expect to find it. And it's not there.
That's because the IDE actually sets the current directory to somewhere else. (I'm not sure where -- I don't use Visual Studio.) If you do this:
cout << std::file_system::current_path() << endl;
it should give you a clue.

Related

Why does the starting directory matter when launching a OpenGL executable?

I am doing a openGL porject and readering my shaders from .vert and .frag glsl files. I am using CMake with an extension for auto ninja file generation as well. This is my readfile code:
std::string readFile(const char *filePath) {
std::string content;
std::ifstream fileStream(filePath, std::ios::in);
if(!fileStream.is_open()) {
std::cerr << "Could not read file " << filePath << ". File does not exist." << std::endl;
return "";
}
std::string line = "";
while(!fileStream.eof()) {
std::getline(fileStream, line);
content.append(line + "\n");
}
fileStream.close();
return content;
}
I spent a long time thinking I was crazy because from my build dir I was doing ./app/NAME_OF_APP.exe but the shaders were not being applied just black. It was only when I clicked on it from within the file explorer that it worked so I cd'ed into build/app/ and called ./NAME_OF_APP.exe and boom it worked. Why is this behavior happening? does the starting dir effect the relative pathing of the actual application? seems odd to me. thanks.
If you run a program from a command line the current directory is whatever directory is current in that command prompt. If you run a program from explorer the current directory is the directory that houses the executable.
You could use a function like GetModuleFileName (with NULL as the hModule parameter) to get the path to the executable and then chop off at the last \ and then change to that directory and then the program would not depend on the directory the program is run from.

ifstream won't find file even though it's in the same directory

I've got a method to read a vector of bools from a file:
std::vector<bool> OPCConnector::getAlarmVector() {
std::vector<bool> data;
std::ifstream DataFile(filepath);
if (DataFile) {
bool value;
while (DataFile >> value) {
data.push_back(value);
std::cout << value;
}
}
return data;
}
The filepath variable is an object property that is assigned through the constructor:
OPCConnector::OPCConnector(std::string fpth) {
filepath = fpth;
}
And in the main() function, the constructor is called:
std::vector<bool> activations;
std::string filepath = "alarmes.txt";
OPCConnector opcc = OPCConnector(filepath);
activations = opcc.getAlarmVector();
Now, I've checked what the folder of the executable is via GetModuleFileNameA(), and I made sure that the file is in the same directory and has the same name (also, I made sure that the extension isn't part of the file name, like "alarmes.txt.txt").
I debugged the first method getAlarmVector() and it never gets past the if (DataFile) condition, as if it won't find file.
I run the code using Visual Studio 2019, and nothing happens. The vector remains empty. Error is No such file or directory.
Default working directory is $(ProjectDir) and it's exactly where my file is.
Edit: I've also tried using both relative and absolute paths, none work.
Edit 2: I've also checked the directory using GetCurrentDirectory() and copied the .txt file there too, and it isn't working.
SOLUTION: Strangely enough, I deleted the file and created it again with the same name, and it worked. Thanks for the answers.
My guess: your current working directory isn't what you think it is, especially if you're running from an IDE. I know of several IDEs where the current working directory is some build directory (it varies by IDE) unless you specifically change it.
I'm fairly sure Visual Studio is one such IDE.
Here's a tiny example program I wrote;
$ cat Foo.cpp
#include <iostream>
#include <fstream>
int main(int, char **) {
std::ifstream file { "Foo.cpp" };
if (file) {
std::cout << "File opened.\n";
}
else {
std::cout << "File not opened.\n";
}
}
Compile and run it:
$ g++ --std=c++17 Foo.cpp -o Foo && Foo
File opened.
Current folder and folder-of-exe-file are different things (sometimes). Try to specify full name of file (with disk, all folders, etc.).
You can check errors of file open operation by calling
if (!DataFile) { ... }
The std::filesystem library can help you resolve file and path related issues.
#include <filesystem>
// (in some function)
std::filesystem::path filepath = "alarmes.txt";
if ( !exists(filepath) )
{
std::cout << "File path " << filepath << " at absolute location "
<< absolute(filepath) << " does not exist\n";
}
See it on Compiler Explorer
You can get an error code (and get a description of error in internet) if you use C-function fopen. If open is failed, you get the nullptr as result of fopen and errno will contain code of error.

Create a file at a given path using C++ in Linux

I want to create a file at a given path that is relative to the current directory. The following code behaves erratically. I some times see the file created and some times do not. That may be because of the change in the current directory. Here's the code.
//for appending timestamp
timeval ts;
gettimeofday(&ts,NULL);
std::string timestamp = boost::lexical_cast<std::string>(ts.tv_sec);
//./folder/inner_folder is an existing directory
std::string filename = "./folder/inner_folder/abc_"+timestamp+ ".csv";
std::ofstream output_file(filename);
output_file << "abc,efg";
output_file.close();
Now, the problem is the file is created only in some cases. That is when I have as a command line argument an input file from the current directory, it works fine.
./program input_file
If I have something like this, it does not work
./program ./folder1/input_file
I tried giving the full path as an argument for ofstream, I still don't see the files created.
What is the correct way to do this? Thanks
ofstream will not create missing directories in the file path, you must ensure the directories exist and if not create them using OS specific api or boost's file system library.
Always check the result of IO operations, and query system error code to determine reason for failures:
if (output_ file.is_open())
{
if (!(output_file << "abc,efg"))
{
// report error.
}
}
else
{
const int last_error = errno;
std::cerr << "failed to open "
<< filename
<< ": "
<< strerror(last_error)
<< std::endl;
}

How to "select" current directory?

I am writing a proportion calculator. At the beginning of the program, it loads a ascii text art picture from a .txt in the same folder.
Here is how I am doing it:
//Read picture
string line;
ifstream myfile("/Users/MYNAME/Desktop/MathScripts/Proportions/Value Finder/picture.txt");
if (myfile.is_open()) {
while (!myfile.eof()) {
getline(myfile, line);
cout << line << endl;
}
myfile.close();
} else cout << "Unable to load picture!!!" << endl;
//Finish reading txt
I heard how if the .txt is in the same folder, that you can just use the name and not have to say the directory. Meaning instead of
/Users/MYNAME/Desktop/MathScripts/Proportions/Value Finder/picture.txt
I could just use "picture.txt". That doesn't work for me, and I want the user to be able to move around the "Value Finder" folder without having to edit any code.
I am on Mac and I am using CodeRunner; anything odd?
Please do not tell me to make sure that picture.txt is in the same folder as my code. It is.
In order to open picture.txt without using a fully qualified path it has to reside in the current working directory. When an IDE launches an application it it sets the current working directory to the same one the application resides in. If picture.txt resides in a different directory than the application you will not be able to open it with just it's name. If you need to get the current working directory you can call getcwd like so.
char temp[MAXPATHLEN];
getcwd(temp, MAXPATHLEN);
If you want to allow the user to specify which directory picture.txt is in you can let them pass an argument on the command line. You can then create a fully qualified path with the supplied directory and the picture filename.
int main(int argc, const char **argv)
{
// Add some logic to see if the user passes a path as an argument
// and grab it. here we just assume it was passed on the command line.
const string user_path = arg[1];
//Read picture
string line;
ifstream myfile(user_path + "/picture.txt");
if (myfile.is_open())
{
while (!myfile.eof()) {
getline(myfile, line);
cout << line << endl;
}
myfile.close();
}
else
{
cout << "Unable to load picture!!!" << endl;
}
//Finish reading txt
return 0;
}
Now you can do something like this:
myapp "/user/USERNAME/Desktop/MathScripts/Proportions/Value Finder"
and it will look in that directory for the picture.txt file. (Quotes are required because there is a space in the pathname).
Note: You can call setcwd() to change the current working directory of the application.

C++ code not finding file

I'm trying to create a program that will concatenate (add two lists of integers together)
each list is stored as a text file. I want the C++ program to open list1.txt and list2.txt
I can't actually get it to work though. I've put two lists of integers names list1 and list2 respectively however I'm getting the output cannot find list1.
#include <iostream>
#include <fstream>
#include <ostream>
using namespace std;
int main()
{
ifstream findlist1("list1.txt", ios::in | ios::binary);
if(!findlist1)
{
cout << "Cannot find list 1.\n";
return 1;
}
ifstream findlist2("list2.txt", ios::in | ios::binary);
if(!findlist2)
{
cout << "Cannot find list 2.\n";
return 1;
}
ofstream out("list3out.txt", ios::out | ios::binary);
if(!out)
{
cout << "Unable to output file ";
return 1;
}
out << in1.rdbuf();
out << " " << flush;
out << in2.rdbuf();
return 0;
}
EDIT = SOLUTION:
My files were called test1.txt and were therefore showing up to the program as test1.txt.txt
The code looks fine, you may try using absolute path or put the files in the same directory of executable
If you are using Visual Studio, all relative paths are relative to the project's working directory. The default seems to be the project directory - meaning that if in C:\SolutionX\ProjectY\Build\ProjectY.exe you try to open the path "file.txt", Windows will look for C:\SolutionX\ProjectY\file.txt. If you'd like to change this directory, the setting is in the project's Configuration Properties under Debugging as "Working Directory".
Note that if you double click the executable manually rather than running it through Visual Studio, its working directory will be its current location. If instead you run the program from a command line, the working directory will be your working directory in the command line.