我知道之前已经问过这个问题(答案是realpath()
),但我的要求有点不同。 realpath()
是非标准功能。我需要ANSI兼容函数来返回完整路径形式的相对路径。它自身的功能不必是c89
的一部分,但代码必须符合100%c89
。
感谢您的帮助。
编辑:应该用于类UNIX系统。此时我并不关心窗户。
答案 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的可用性。它不是生产代码的推荐解决方案,但它可能适合您的特定约束。以下是步骤:
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;
}