所以我正在尝试创建一个程序来改变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
答案 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
指针。HOME
的环境变量通常会扩展到用户的主目录。HOME
环境变量。但事实并非如此。如果不成立,通常会有一个名为HOMEDRIVE
的环境变量扩展到主驱动器(如果主目录位于C驱动器上,则为"C:"
),另一个名为{{的变量1}}扩展到用户主目录而没有名为附加的驱动器。HOMEPATH
字符和子目录名来构造路径。例如'/'
。"/user/home"
调用)接受fopen()
和'/'
作为路径分隔符。还要记住'\\'
不会构建目录。要使fopen()
成功,目录fopen("/temp/x.dat", "w")
必须已存在,而/temp
只会尝试在其中创建fopen()
。
但请注意,使用环境变量不是获取用户主目录的特别好方法 - 特别是在Windows下 - 因为用户可以更改环境变量。获取主目录的其他方法将更加健壮,但通常也特定于unix或windows(例如,特定于OS的API函数,而不是使用x.dat
)。我会把这些功能作为一种练习。
无论你构建路径的程度如何,在传递它返回到其他读写函数的句柄之前检查getenv()
是否成功仍然是个好主意。