【问题标题】:Getting the absolute path from a relative path [closed]从相对路径获取绝对路径[关闭]
【发布时间】:2016-03-19 07:25:48
【问题描述】:

我知道以前有人问过这个问题(答案是realpath()),但我的要求有点不同。 realpath() 是一个非标准函数。我需要一个符合 ANSI 的函数来从相对路径返回完整路径。它自身的函数不必是c89 的一部分,但代码必须100% 符合c89

感谢您的帮助。

编辑:应该用于类 UNIX 系统。在这一点上,我真的不在乎窗户。

【问题讨论】:

  • 问题是绝对路径的定义取决于操作系统。所以不可能有一个可移植的解决方案(至少没有一堆#ifdefs,或者基于操作系统的运行时检查)。
  • 谢谢你。我将针对 UNIX 系统更新我的问题
  • @Ankush:如果您使用的是 Unix(类)系统,那么您已经是 POSIX,并且应该已经拥有 realpath()。如果您的程序无论如何都是 POSIX 特定的,那么您为什么要严格遵守 ANSI/ISO?
  • @Ankush:你什么意思? POSIX 是一个非常受人尊敬的标准。我的意思是,那里的每个操作系统都实现了它……当然,除了 Microsoft Crapware(R)。 ANSI/ISO C方式太便携了。它甚至可以在最小的嵌入式系统上工作!如果标准库对您来说不够便携,它甚至定义了“独立”模式。另一方面,如果您需要最少量的操作系统服务,您就需要一个操作系统 API,不是吗? POSIX 是终极的 Unix API,因此,如果您需要 Unix 服务,例如realpath(),则需要 POSIX。
  • 多么糟糕的任务。

标签: c path filepath


【解决方案1】:

没有任何 C 语言标准定义处理目录或路径的函数。如果不了解操作系统和特定于操作系统的系统调用或库函数,您将无法完成您的目标。

您指定您的程序以 Unix 操作系统为目标。有数百种 Unix 风格(参见http://www.levenez.com/unix/ 和这个 34 页的时间线:http://www.levenez.com/unix/unix_a4.pdf)。为 unix 目标处理目录的最便携方式是使用 Posix 标准化系统调用。

Posix 不是语言的扩展,它是操作系统结构和接口的规范。它在标准头文件和库中实现。您的程序可以遵守 c89、c90、c99 或 c11 标准并使用 Posix 系统调用。 gcc 将在 Posix 系统上启用 glibc 的 Posix 部分,除非您指示它限制对标准 C 库的支持。

Posix 在 2001 年标准化了 realpath,它几乎可以在所有当前的类 unix 系统上使用。如果您不能使用它,您将使用getcwd()stat()readlink() 和其他系统调用重写它的功能,您的代码将不那么可移植。

手册页在这里:http://man7.org/linux/man-pages/man3/realpath.3.html

编辑:有一个扭曲的解决方案不使用明确的 Posix 支持,而是隐含地依赖于 Posix shell 的可用性。它不是生产代码的推荐解决方案,但它可能适合您的特定限制。步骤如下:

  • 使用strrchr,提取文件名的初始路径部分。
  • 创建一个将当前目录更改为该目录并将当前目录打印到临时文件的命令
  • 打开并读取该临时文件的内容。
  • 删除临时文件。
  • /分隔符连接路径和文件名

请注意,在子 shell 中更改当前目录不会影响程序自己的当前目录。

编辑:实现上述内容有点棘手。您需要处理一些特殊情况。这是一个例子:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static char *my_realpath(const char *path) {
    const char *lastsep, *fname;
    char *cmd, *dir, *newdir;
    int pathlen, fnamelen, rc, size, pos, len;
    char buf[256];
    char tmpfile[L_tmpnam];
    FILE *fp;

    if (!tmpnam(tmpfile))
        return NULL;

    pathlen = 0;
    lastsep = strrchr(path, '/');
    if (lastsep) {
        pathlen = lastsep - path;
        if (pathlen == 0) {
            /* special case the root directory */
            pathlen = 1;
        }
    }
    fname = path + pathlen;
    fnamelen = strlen(fname);
    if (!strcmp(fname, ".")
    ||  !strcmp(fname, "/.")
    ||  !strcmp(fname, "..")
    ||  !strcmp(fname, "/..")) {
        pathlen += fnamelen;
        fname += fnamelen;
        fnamelen = 0;
    }
    if (*fname == '/') {
        fname++;
        fnamelen--;
    }
    if (pathlen > 0) {
        size = strlen("cd ") + pathlen + strlen("; pwd > ") + strlen(tmpfile) + 1;
        cmd = malloc(size);
        if (!cmd)
            return NULL;
        sprintf(cmd, "cd %.*s; pwd > %s", pathlen, path, tmpfile);
    } else {
        size = strlen("pwd > ") + strlen(tmpfile) + 1;
        cmd = malloc(size);
        if (!cmd)
            return NULL;
        sprintf(cmd, "pwd > %s", tmpfile);
    }
    rc = system(cmd);
    free(cmd);
    if (rc != 0)
        return NULL;

    fp = fopen(tmpfile, "r");
    size = pos = 0;
    dir = NULL;
    while (fgets(buf, sizeof buf, fp)) {
        len = strcspn(buf, "\n");
        size += len + 1;
        newdir = realloc(dir, size + fnamelen + 1);
        if (!newdir) {
            free(dir);
            dir = NULL;
            break;
        }
        dir = newdir;
        memcpy(dir + pos, buf, len);
        pos += len;
        dir[pos] = '\0';
        if (buf[len] == '\n')
            break;
    }
    fclose(fp);
    remove(tmpfile);
    if (dir != NULL) {
        if (pos > 0 && dir[pos - 1] != '/' && fnamelen > 0) {
            dir[pos++] = '/';
        }
        strcpy(dir + pos, fname);
    }
    return dir;
}

int main(int argc, const char *argv[]) {
    int i;
    char *absolute_path;

    for (i = 1; i < argc; i++) {
        errno = 0;
        absolute_path = my_realpath(argv[i]);
        if (absolute_path == NULL) {
            printf("my_realpath(\"%s\") -> NULL: %s\n", argv[i], strerror(errno));
        } else {
            printf("my_realpath(\"%s\") -> \"%s\"\n", argv[i], absolute_path);
            free(absolute_path);
        }
    }
    return 0;
}

【讨论】:

  • 感谢您的回答!只是想知道我的编译器标志上的 -std=c89-ansi 是否会被视为限制对 c std 库的支持?
  • 另外我只是想知道realpath() 是否不是语言的扩展,为什么它要求我在编译时使用-D 标志,或者使用#define GNU_SOURCE。这让我有点困惑
  • 语言扩展(如 gnu cc 的 typeof 运算符)与可能需要一些编译器配置的特定目标环境之间存在区别。
  • 还有-std-c89?这是否限制了 c 标准库?
  • @Ankush:是的,恐怕gcc -std=c89 限制了支持的语言和默认库支持。
猜你喜欢
  • 1970-01-01
  • 2022-06-10
  • 2011-08-11
  • 2010-09-21
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
相关资源
最近更新 更多