How to open a file no matter where I run my program from?

148 Views Asked by At

fopen always tries to load files from the working path. To reproduce the bug, I wrote a demo code below:

#include<stdio.h>
int main(void) {
    FILE* my_file = NULL;
    const char* file_name = "some_file.dat";
    errno_t errcode = fopen_s(&my_file, file_name, "rb");
    if (errcode != 0) {
        printf("Cannot open the file `%s`. Error code = %d", file_name, errcode);
    }
    else {
        puts("Success!!");
    }
    return errcode;
}
  1. I have a file d:\path1\some_file.dat and a directory d:\path2. Then I compile the code above to the program named D:\path1\myprogram.exe.
  2. Input command
    cd d:\path1
    myprogram
    
    and the program will print "Success!!".
  3. Input command
    cd d:\path2
    ..\path1\myprogram
    
    and the program will print "Cannot open the file some_file.dat. Error code = 2".

My question is how to successfully open the file no matter where I run my program from. If fopen can't do this, is there a library that can do it?

Reply to the comments:

I know that fopen can load files from absolute paths but I want to make my program portable. myprogram.exe and some_file.dat are always in the same path.

2

There are 2 best solutions below

0
Weather Vane On BEST ANSWER

Assuming that the file is in the same folder as the executable.
You can use the argv[0] passed to the program – which is the executable name.
I have shown it run three ways: two from the console, and one from GUI file manager.

#include <stdio.h>
#include <string.h>

#define SLASH_CHAR  '\\'    // Windows
//#define SLASH_CHAR  '/'    // Linux

int main(int argc, char *argv[]) {

    const char* file_name = "some_file.dat";    // desired filename
    printf("argv[0] = %s\n", argv[0]);          // the executable
    
    // allocate enough memory to create a file name
    char *fname = malloc(strlen(argv[0]) + strlen(file_name) + 1);
    if(fname == NULL)
        return 1;

    // find the last path separator in the executable (if any)
    char *slash_ptr = strrchr(argv[0], SLASH_CHAR);
    if (slash_ptr == NULL) {
        // just use the file name
        strcpy(fname, file_name);
    }
    else {
        // create a new file name
        strcpy(fname, argv[0]);
        size_t slash_ind = slash_ptr - argv[0];
        strcpy(fname + slash_ind + 1, file_name);
    }
    
    printf("fname = %s\n", fname);
    free(fname);
    getchar();
    return 0;
}

Run from the console current directory

argv[0] = test
fname = some_file.dat

Run in the console from a parallel folder

argv[0] = ..\temp\test
fname = ..\temp\some_file.dat

Run from Windows file manager

argv[0] = F:\TEMP\test.exe
fname = F:\TEMP\some_file.dat
0
codeling On

fopen can take either a full, absolute path to a file, or, if it gets an incomplete path, it has to assume that the path is relative; relative paths are always relative to the current working directory.

In short, fopen, just as any other facility to open files, cannot magically guess what file you mean; it needs to know exactly where it should look for the file.

You can specify a full path like this:

    // ....
    const char* file_name = "D:\\path1\\some_file.dat";
    // ....

From your comments, your problem however seems to be that you don't know the exact full path to the file at compile time; so you probably want to get the path to the file from the user of the program somehow;

One typical way of doing this is to read it from a parameter at the command line:

#include<stdio.h>
int main(int argc, char** argv) {
    FILE* my_file = NULL;
    if (argc < 2) {
        puts("You need to specify a filename!");
        return 2;
    }
    errno_t errcode = fopen_s(&my_file, argv[1], "rb");
    if (errcode != 0) {
        printf("Cannot open the file `%s`. Error code = %d", argv[1], errcode);
    }
    else {
        puts("Success!!");
    }
    return errcode;
}

Running:

Test case 1:

> cd d:\path1
> myprogram some_file.dat
Success!!

Test case 2 (relative):

> cd d:\path2
> ..\path1\myprogram ..\path1\some_file.dat
Success!!

Test case 2 (absolute):

> cd d:\path2
> ..\path1\myprogram d:\path1\some_file.dat
Success!!

If your some_file.dat always resides in the same folder as your program, there are other answers available on similar questions.

You can also combine a path specified at the command line with a hard-coded file name of course, for that, look into sprintf function if you need to stick to C language, or e.g. std::ostringstream if you can actually use C++ as your tags suggest.