1

我一直试图让这段代码工作几个小时!我需要做的就是打开一个文件,看看它是否真实且可读。我是 C 的新手,所以我确定我缺少一些愚蠢的东西。这是代码(速记,但已复制):

#include <stdio.h>

main() {
    char fpath[200];
    char file = "/test/file.this";
    sprintf(fpath,"~cs4352/projects/proj0%s",file);

    FILE *fp = fopen(fpath,"r");
    if(fp==NULL) {
        printf("There is no file on the server");
        exit(1);
    }
    fclose(fp);
    //do more stuff
}

我还验证了路径是否正确指定了我具有读取权限的真实文件。还有其他想法吗?

编辑 1:我知道 fpath 以“~cs4352/projects/proj0/test/file.this”结尾

编辑2:我也尝试过使用绝对文件路径。在这两种情况下,我都可以通过 ls 验证路径是否正确构建。

编辑 3:errno2...我目前正在尝试跟踪这在谷歌中的含义。

编辑 4: 好的,errno其中 2 是“没有这样的文件或目录”。当 fopen 中的参考路径是“/home/courses1/cs4352/projects/proj0/index.html”时,我得到了这个,我验证它确实存在并且我拥有它的阅读权限。至于下面列出的 C 代码,其中可能存在一些语义/新手错误,但 gcc 没有给我任何编译时警告,并且代码完全按照它应该的方式工作,只是它说它一直errno在吐 2。换句话说,我知道所有的字符串/字符数组都正常工作,但唯一可能成为问题的是fopen()调用。

解决方案: 好的,access() 过程对我帮助最大(我仍在使用它,因为它的代码更少,更不用说更优雅的方式了)。问题实际上来自于我没有向大家解释过的东西(因为直到我使用 access() 才看到它)。为了导出文件,我使用 strtok() 拆分字符串并且只在“\n”上拆分,但因为这是一个 UNIX 系统,我还需要在其中添加“\r”。一旦我解决了这个问题,一切就都到位了,我确信 fopen() 函数也能正常工作,但我还没有测试过。

感谢大家的有益建议,尤其感谢 Paul Beckingham 找到了这个绝妙的解决方案。

干杯!

4

7 回答 7

6
  1. "~" 是由 shell 扩展的,而不是由 fopen 扩展的。
  2. 要测试文件的存在性和可读性,请考虑使用 POSIX.1 “访问”功能:
#include <unistd.h>

if (访问 ("/path/to/file", F_OK | R_OK) == 0)
{
  // 文件存在且可读
}
于 2009-02-07T04:24:22.053 回答
5

首先,file需要声明为char*or const char*,而不是char像你写的那样简单。但这可能只是一个错字,编译器至少应该在那里给出警告。

其次,使用绝对路径(或相对于当前目录的路径),而不是带有~. 相应主目录的替换~cs4352通常由 shell 完成,但您是直接打开文件。所以你试图在~cs4352你当前工作目录的子目录中打开一个文件,我猜这不是你想要的。

于 2009-02-07T03:59:54.147 回答
2

其他人可能已经产生了等价物(例如,每个现代 shell),但这里有一些代码可以用 ~ 或 ~user 符号扩展文件名。

#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif

#include <assert.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

char *relfname(const char *name, char *buffer, size_t bufsiz)
{
    assert(name != 0 && buffer != 0 && bufsiz != 0);
    if (name[0] != '~')
        strncpy(buffer, name, bufsiz);
    else
    {
        const char *copy;
        struct passwd *usr = 0;
        if (name[1] == '/' || name[1] == '\0')
        {
            usr = getpwuid(getuid());
            copy = &name[1];
        }
        else
        {
            char username[PATH_MAX];
            copy = strchr(name, '/');
            if (copy == 0)
                copy = name + strlen(name);
            strncpy(username, &name[1], copy - &name[1]);
            username[copy - &name[1]] = '\0';
            usr = getpwnam(username);
        }
        if (usr == 0)
            return(0);
        snprintf(buffer, bufsiz, "%s%s", usr->pw_dir, copy);
    }
    buffer[bufsiz-1] = '\0';
    return buffer;
}

#ifdef TEST
static struct { const char *name; int result; } files[] =
{
    { "/etc/passwd", 1 },
    { "~/.profile", 1 },
    { "~root/.profile", 1 },
    { "~nonexistent/.profile", 0 },
};

#define DIM(x)  (sizeof(x)/sizeof(*(x)))

int main(void)
{
    int i;
    int fail = 0;
    for (i = 0; i < DIM(files); i++)
    {
        char buffer[PATH_MAX];
        char *name = relfname(files[i].name, buffer, sizeof(buffer));
        if (name == 0 && files[i].result != 0)
        {
            fail++;
            printf("!! FAIL !! %s\n", files[i].name);
        }
        else if (name != 0 && files[i].result == 0)
        {
            fail++;
            printf("!! FAIL !! %s --> %s (unexpectedly)\n", files[i].name, name);
        }
        else if (name == 0)
            printf("** PASS ** %s (no match)\n", files[i].name);
        else
            printf("** PASS ** %s -> %s\n", files[i].name, name);
    }
    return((fail == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}

#endif
于 2009-02-07T07:05:51.760 回答
1

您可以尝试检查errno有关您没有获得有效FILE*.

顺便说一句——在 unix 中,全局值errno是由一些库和系统调用设置的,当它们需要返回更多信息而不仅仅是“它不起作用”时。仅保证在相关调用后立即良好。

于 2009-02-07T03:58:49.313 回答
0

char file = "/test/file.this";

你可能想要

char *file = "/test/file.this";

于 2009-02-07T03:45:48.980 回答
0

你确定你不是那个意思

~/cs4352/projects/proj0%s"

为您的主目录?

于 2009-02-07T03:47:07.637 回答
0

总结一下:

  1. 使用 char *file=/test/file.this";
  2. 不要指望 fopen() 对 ~ 进行 shell 替换,因为它不会。使用完整路径或使用相对路径,并确保当前目录是适当的。
  3. 错误 2 表示未找到该文件。由于此列表中的第 2 项,未找到它。

为了获得额外的荣誉,像这样使用 sprintf() 写入分配在堆栈上的缓冲区是一种危险的习惯。至少查找并使用 snprintf()。

正如这里的其他人所提到的,使用 access() 将是一种更好的方式来做你在这里尝试的事情。

于 2009-02-07T04:35:08.803 回答