无论我从哪里运行我的程序,如何打开文件?

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

提问人: 提问时间:11/3/2023 更新时间:11/6/2023 访问量:121

问:

fopen始终尝试从工作路径加载文件。为了重现这个错误,我在下面编写了一个演示代码:

#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. 我有一个文件和一个目录。然后,我将上面的代码编译为名为 .d:\path1\some_file.datd:\path2D:\path1\myprogram.exe
  2. 输入命令,程序将打印“成功!!”。
    cd d:\path1
    myprogram
    
  3. 输入命令,程序将打印“无法打开文件。错误代码 = 2”。
    cd d:\path2
    ..\path1\myprogram
    
    some_file.dat

我的问题是,无论我从哪里运行我的程序,如何成功打开文件。如果不能做到这一点,有没有一个图书馆可以做到这一点?fopen

回复评论:

我知道可以从绝对路径加载文件,但我想让我的程序可移植。 并且始终在同一条路径上。fopenmyprogram.exesome_file.dat

c 海湾合作委员会 fopen

评论

0赞 NathanOliver 11/3/2023
如果默认方式失败,您可以随时询问文件位置。
1赞 Weather Vane 11/3/2023
您可以尝试查看作为文件名的基础。argv[0]
1赞 Eric Postpischil 11/3/2023
如果路径指示要从根目录开始,则称为绝对路径。如果没有,则称为相对。使用绝对路径打开文件从绝对路径指示的根目录开始。使用相对路径打开文件从进程的工作目录开始。 是相对路径。将其更改为绝对路径。更改为 .some_file.datconst char* file_name = "some_file.dat";const char *file_name = "d:\\path1\\some_file.dat";
0赞 codeling 11/3/2023
据我所知,您目前的代码是纯 C。你给它 c++ / visual-c++ 标签有什么具体原因吗?
1赞 codeling 11/3/2023
“我知道 fopen 可以从绝对路径加载文件,但我想让我的程序可移植。”- 如果您无法指定绝对路径,也不知道该文件的正确相对路径,您的程序应该如何查找该文件?神奇地,不知何故......?

答:

1赞 codeling 11/3/2023 #1

fopen可以采用文件的完整绝对路径,或者,如果它得到一个不完整的路径,它必须假定该路径是相对的;相对路径始终相对于当前工作目录。

简而言之,就像任何其他打开文件的工具一样,无法神奇地猜出您的意思;它需要确切地知道它应该在哪里查找文件。fopen

您可以指定如下所示的完整路径:

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

但是,从您的评论来看,您的问题似乎是您在编译时不知道文件的确切完整路径;因此,您可能希望以某种方式从程序用户那里获取文件的路径;

执行此操作的一种典型方法是从命令行的参数中读取它:

#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;
}

运行:

测试用例 1:

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

测试用例 2(相对):

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

测试用例 2(绝对):

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

如果您始终与程序位于同一文件夹中,则对于类似问题还有其他答案some_file.dat

当然,您也可以将命令行中指定的路径与硬编码文件名相结合,为此,如果您需要坚持使用 C 语言,请查看函数,或者例如 如果您实际上可以按照标签的建议使用 C++。sprintfstd::ostringstream

0赞 Weather Vane 11/4/2023 #2

假设文件与可执行文件位于同一文件夹中。
您可以使用传递给程序的 – 这是可执行文件名称。
我已经展示了它以三种方式运行:两种来自控制台,一种来自 GUI 文件管理器。
argv[0]

#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;
}

从控制台当前目录运行

argv[0] = test
fname = some_file.dat

在控制台中从并行文件夹运行

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

从 Windows 文件管理器运行

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