从相对路径获取绝对路径

时间:2016-03-19 07:25:48

标签: c path filepath

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

感谢您的帮助。

编辑:应该用于类UNIX系统。此时我并不关心窗户。

1 个答案:

答案 0 :(得分: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库的支持。

2001年Posix标准化realpath,它几​​乎适用于所有当前类似unix的系统。如果你不能使用它,你将使用getcwd()stat()readlink()和其他系统调用重写其功能,并且你的代码将不那么便携。

手册页位于:http://man7.org/linux/man-pages/man3/realpath.3.html

编辑:有一个扭曲的解决方案不使用显式的Posix支持,但隐式依赖于Posix shell的可用性。它不是生产代码的推荐解决方案,但它可能适合您的特定约束。以下是步骤:

  • with 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;
}
相关问题