【问题标题】:fopen in C on solaris在 solaris 上用 C 语言打开 fopen
【发布时间】:2010-10-06 02:28:56
【问题描述】:

我一直试图让这段代码工作几个小时!我需要做的就是打开一个文件,看看它是否真实且可读。我是 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:errno 是 2...我目前正在尝试在 google 中跟踪这意味着什么。

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

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

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

干杯!

【问题讨论】:

  • 我真的认为操作系统实际上不理解“~cs4352”。用真实路径替换它。 ~cs4352 只是被外壳替换。但这里没有贝壳。但正如你所说,你也尝试过绝对路径。我想这是另一个问题
  • 系统error.h应该有一个errno的列表。在 MacOS X 上,2 是“没有这样的文件或目录”。我喜欢乔和某某给出的答案。
  • 只使用 perror 它以人类可读的形式显示错误,或者使用 strerror 来翻译 errno
  • @litb:你每天都会学到一些东西。谢谢。

标签: c file solaris


【解决方案1】:
  1. “~”由shell扩展,fopen不扩展。
  2. 要测试文件的存在性和可读性,请考虑使用 POSIX.1“访问”功能:
#include if (访问 ("/path/to/file", F_OK | R_OK) == 0) { // 文件存在且可读 }

【讨论】:

  • 非常感谢您给我一个 POSIX 解决方案!!!这很好用。祝你有美好的一天!
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    其他人可能已经产生了等价物(例如每个现代 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
    

    【讨论】:

    • 不错的 +1 努力(尚未测试该代码,但看起来很清楚:p)。今天我听说了“wordexp”posix 函数,它可以做到这一点以及更多(其他扩展)。也许您还不知道它,并且可能会发现它也很有用:)
    • 依稀记得它存在;我从未使用过它 - 或 fnmatch() 函数。它们来自 POSIX.2,往往会被遗忘。
    • 而且,FWIW,此代码使用 /etc/passwd(或密码数据库)中的主目录定义; wordexp() 使用 $HOME 的当前值(通常但不一定是同一件事)。当然,这种变化是微不足道的。
    【解决方案4】:

    您可以尝试检查errno,以了解有关您没有获得有效FILE* 的原因的更多信息。

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

    【讨论】:

      【解决方案5】:

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

      你可能想要

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

      【讨论】:

        【解决方案6】:

        你确定你不是那个意思

        ~/cs4352/projects/proj0%s"

        你的主目录?

        【讨论】:

          【解决方案7】:

          总结一下:

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

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

          正如这里的其他人所提到的,使用 access() 将是一种更好的方式来执行您在此处尝试的操作。

          【讨论】:

            猜你喜欢
            • 2018-04-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-07-28
            • 1970-01-01
            • 1970-01-01
            • 2014-12-13
            • 1970-01-01
            相关资源
            最近更新 更多