C fopen到特定目录

时间:2017-09-01 22:01:20

标签: c linux windows

所以我正在尝试创建一个程序来改变fopen()函数的文件目录,具体取决于系统是Windows还是Linux。

在我的主要功能中,我通过

确定操作系统
#ifdef __unix
    printf("linux machine \n");
    windows = false;
    linux = true;
#endif

#ifdef _WIN32   
    printf("windows machine \n");
    windows = true;
    linux = false;
#endif

然后在我的功能中我打开文件

FILE *fp, *fpo;
    if ((windows == true) && (linux == false)) 
    {
        fp = fopen("/temp/coursein/p1-in.txt", "r");
        fpo = fopen("/temp/courseout/p1-out.txt", "w");
    }
    else
    {
        fp = fopen("~/temp/coursein/p1-in.txt", "r");
        fpo = fopen("~/temp/coursein/p1-in.txt", "w");
    }

我已经尝试了“/”“//”“\”“\”的每个变体我想到了并且在linux上我得到了一个seg错误,在visual studio中我得到了一个Debug Assertion Failed。 表达式:stream!= nullptr

4 个答案:

答案 0 :(得分:2)

您最有可能在Linux版本中崩溃,因为在将流指针传递给流函数之前,不会检查NULL的流指针。

假设fopen()总是成功,编程习惯很差。始终检查fopen()和许多其他库函数的返回值。

Linux版本中似乎有一个拼写错误:fpo = fopen("~/temp/coursein/p1-in.txt", "w");应该是fpo = fopen("~/temp/courseout/p1-out.txt", "w");

此外,fopen不会将初始~/扩展到用户的主目录,shell会在命令行字上执行此操作,而不是标准C库。在unix中,您可以使用getenv("HOME")来检索用户的主目录,并使用snprintf来组合要打开的文件名。您可以为fopen编写一个包装函数来处理系统特定的行为并返回流句柄。

有一个更简单的修复:您可以创建相对于当前目录的路径:

    fp = fopen("temp/coursein/p1-in.txt", "r");
    fpo = fopen("temp/courseout/p1-out.txtt", "w");

    fp = fopen("coursein/p1-in.txt", "r");
    fpo = fopen("courseout/p1-out.txt", "w");

只要您从适当的目录运行程序或使用chdir更改启动函数中的当前目录,您可以在linux和Windows中运行它,您可以在其中测试运行的操作系统。

答案 1 :(得分:1)

有很多方法可以解决这个问题,但关键在于使用getenv获取所需的基本目录。它适用于windows和unix。您只需检查并找到每个操作系统下所需的环境变量的正确名称。

例如,要从用户主目录开始构建路径,可以在Linux上使用环境变量HOME,在Windows上使用USERPROFILE

要将代码设置为自动使用相应的目录,通常会使用预处理器检查来检查操作系统,然后设置一个标识操作系统的定义,以便在代码的其余部分中使用。 (当然,您可以反复使用相同的测试,但标准做法只是定义操作系统的标识符,例如Windows上的HAVEWIN或各种Unix风格的HAVEUNIX

我通常将其分为两部分,但您可以一次性完成所有操作,例如:

#if defined (_WIN32) || defined (_WIN64)
 #define HAVEWIN 1
#elif defined (__unix__)
 #define HAVEUNIX 1
#endif

#ifdef HAVEWIN
 #define HOMEENV "USERPROFILE"
#elif HAVEUNIX
 #define HOMEENV "HOME"
#endif

对于路径名分隔符,Windows将允许您使用POSIX路径名分隔符'/'来分隔路径组件,但当路径包含{时,它可能会变得挑剔(技术术语) {1}}。在这种情况下,Windows路径名解析使用正常的DOS反斜杠(在构建路径时必须转义)会表现得更好。

例如,假设我们对Windows和Linux上名为spaces的文件感兴趣,该文件位于用户的主目录中(例如,Windows上的testfile.txt和Linux上的C:\Users\username ,例如

关于windoze:

/home/username

在Linux上:

C:\users\david>type testfile.txt
hello windows

您可以使用$ cat ~/testfile.txt hello unix 和足以容纳组件的缓冲区构建到达每个路径的路径。 sprintf提供#include <limits.h>常量,以确保您有足够的空间容纳文件名(通常为PATH_MAX个字符)。要构建路径,您可以执行以下操作:

4096

这几乎提供了您尝试对输入和输出文件执行的操作(简短形式)的基础知识。将所有部分组合在一起,您可以执行以下操作:

#include <limits.h>
...
int main (void) {

    char *home = getenv (HOMEENV);
    char filename[PATH_MAX] = "";
    char buf[BUFSIZ] = "";
    FILE *fp;

    if (!home) {
        fprintf (stderr, "user home environment not found.\n");
        return 1;
    }

#ifdef HAVEWIN
    sprintf (filename, "%s\\%s", home, FILENAME);
#elif HAVEUNIX
    sprintf (filename, "%s/%s", home, FILENAME);
#endif
...

示例使用/输出

关于windoze:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define FILENAME "testfile.txt"

#if defined (_WIN32) || defined (_WIN64)
 #define HAVEWIN 1
#elif defined (__unix__)
 #define HAVEUNIX 1
#endif

#ifdef HAVEWIN
 #define HOMEENV "USERPROFILE"
#elif HAVEUNIX
 #define HOMEENV "HOME"
#endif

int main (void) {

    char *home = getenv (HOMEENV);
    char filename[PATH_MAX] = "";
    char buf[BUFSIZ] = "";
    FILE *fp;

    if (!home) {
        fprintf (stderr, "user home environment not found.\n");
        return 1;
    }

#ifdef HAVEWIN
    sprintf (filename, "%s\\%s", home, FILENAME);
#elif HAVEUNIX
    sprintf (filename, "%s/%s", home, FILENAME);
#endif

    if (!(fp = fopen (filename, "r"))) {
        fprintf (stderr, "error: file not found '%s'\n", filename);
        return 1;
    }

    if (fgets (buf, sizeof buf, fp))
        puts (buf);

    return 0;
}

在Linux上:

C:\users\david>Documents\src-c\bin\getenv_testfile.exe
hello windows

正如开头所提到的,有很多方法可以将所有部分组合在一起。您可能会发现将所有不同的windows / unix定义放在顶部更清晰,这样您就不会在$ ./src-c/bin/getenv_testfile hello unix 中有任何预处理器条件,例如

main()

注意:虽然这可能看起来更清晰,但在较大的代码中这实际上是不切实际的)

查看所有答案和评论,如果您有任何其他问题,请与我们联系。

答案 2 :(得分:0)

假设您正在主目录中运行,即〜

fopen的路径来自您当前的工作目录。因此,波浪号是错误的(如上所述,因为它是一个奇怪的壳)。

fp = fopen(&#34; temp / coursein / p1-in.txt&#34;,&#34; r&#34;);

如果需要移动到特定目录,请考虑chdir(Linux上的man 2 chdir)。在打开输出文件之前,不要忘记关于测试fp的其他建议。

答案 3 :(得分:0)

在这两种情况下,崩溃都是因为fopen()调用失败并返回NULL,后续代码正在使用它们而不检查(例如读取或写入)。

unix下的问题是路径中的~由shell扩展(例如在命令行中键入的路径中),而不是由C库扩展。你需要做的是自己扩展路径。

幸运的是,有很多方法可以做你想要的,但在windows和linux下它们通常略有不同。我不会写代码,但我会指出你可以使用的东西。

    getenv()中的
  • <stdlib.h>可用于获取扩展环境变量。例如,getenv("XXX")将返回一个字符串的指针(指向第一个字符),该字符串是windows和unix下名为XXX的环境变量的扩展。如果环境变量NULL不存在,它将返回XXX指针。
  • 在Unix下,名为HOME的环境变量通常会扩展到用户的主目录。
  • 在Windows下,您可能会很幸运并且定义了HOME环境变量。但事实并非如此。如果不成立,通常会有一个名为HOMEDRIVE的环境变量扩展到主驱动器(如果主目录位于C驱动器上,则为"C:"),另一个名为{{的变量1}}扩展到用户主目录而没有名为附加的驱动器。
  • 在unix下,可以通过添加HOMEPATH字符和子目录名来构造路径。例如'/'
  • 在Windows下,路径可以以相同的方式构建,除了Windows API函数(由"/user/home"调用)接受fopen()'/'作为路径分隔符。

还要记住'\\'不会构建目录。要使fopen()成功,目录fopen("/temp/x.dat", "w")必须已存在,而/temp只会尝试在其中创建fopen()

但请注意,使用环境变量不是获取用户主目录的特别好方法 - 特别是在Windows下 - 因为用户可以更改环境变量。获取主目录的其他方法将更加健壮,但通常也特定于unix或windows(例如,特定于OS的API函数,而不是使用x.dat)。我会把这些功能作为一种练习。

无论你构建路径的程度如何,在传递它返回到其他读写函数的句柄之前检查getenv()是否成功仍然是个好主意。