如何在C中检索相对于给定目录的文件路径
How to retrieve filepath relatively to a given directory in C
我正在寻找一种将绝对文件路径转换为相对于特定目录的路径的有效方法。
假设我们必须遵循以下结构:
D:\main\test1\blah.txt
D:\test2\foo.txt
以"D:\main"
为参考目录,则结果为:
- blah.txt => "\test1\blah.txt"
- foo.txt => "..\test2\foo.txt"
有什么线索吗?
备案说明:
好像是:
- 没有统一的API函数(跨平台)来执行这个
此问题已针对其他语言多次提出(尽管大多数答案利用函数 PathRelativePathTo):
- How to get relative path from absolute path
- Getting a file path relative to a particular directory
- How do I get a relative path from one path to another in C#
您在示例中给出了 windows 路径。因此,如果您可以接受使用 WinAPI 函数,则可以使用 PathRelativePathTo.
首先您需要确定文件路径分隔符,如 here 所述。
const char kPathSeparator =
#ifdef _WIN32
'\';
#else
'/';
#endif
然后,您需要编写一个函数来计算规范的绝对文件路径。您将不得不再次使用 #ifdef _WIN32
,因为需要特定的 windows 处理(如果存在 none,则在路径的开头添加当前磁盘)。
之后,删除路径中的所有.
,并删除所有..
及其之前的目录。
写完这个函数后,你需要使用它两次来获取你的起点和目标规范绝对路径,然后正如@Weather Vane 所解释的那样,你需要识别两条路径中的公共部分并添加数字..
连接到目标规范路径的末尾。
这是我能想到的最短的解决方案。
算法其实很简单:
给定 1) 参考路径(结果路径将相对于的路径);和 2) 绝对路径(文件的完整路径):
- 当路径部分相等时:跳过它们
当我们遇到差异时
- 为参考路径的每个剩余部分添加一个“..”
- 从绝对路径添加剩余部分
windows 下的唯一限制是在不同的卷(驱动器号不同)的情况下,在这种情况下我们别无选择,只能 return 原始绝对路径。
跨平台 C 源代码:
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
const char* path_separator = "\";
#else
const char* path_separator = "/";
#endif
#define FILENAME_MAX 1024
char* get_relative_path(char* reference_path, char* absolute_path) {
static char relative_path[FILENAME_MAX];
// init result string
relative_path[0] = '[=10=]';
// check first char (under windows, if differs, we return absolute path)
if(absolute_path[0] != reference_path[0]) {
return absolute_path;
}
// make copies to prevent altering original strings
char* path_a = strdup(absolute_path);
char* path_r = strdup(reference_path);
int inc;
int size_a = strlen(path_a)+1;
int size_r = strlen(path_r)+1;
for(inc = 0; inc < size_a && inc < size_r; inc += strlen(path_a+inc)+1) {
char* token_a = strchr(path_a+inc, path_separator[0]);
char* token_r = strchr(path_r+inc, path_separator[0]);
if(token_a) token_a[0] = '[=10=]';
if(token_r) token_r[0] = '[=10=]';
if(strcmp(path_a+inc, path_r+inc) != 0) break;
}
for(int inc_r = inc; inc_r < size_r; inc_r += strlen(path_r+inc_r)+1) {
strcat(relative_path, "..");
strcat(relative_path, path_separator);
if( !strchr(reference_path+inc_r, path_separator[0]) ) break;
}
if(inc < size_a) strcat(relative_path, absolute_path+inc);
return relative_path;
}
我正在寻找一种将绝对文件路径转换为相对于特定目录的路径的有效方法。
假设我们必须遵循以下结构:
D:\main\test1\blah.txt
D:\test2\foo.txt
以"D:\main"
为参考目录,则结果为:
- blah.txt => "\test1\blah.txt"
- foo.txt => "..\test2\foo.txt"
有什么线索吗?
备案说明:
好像是:
- 没有统一的API函数(跨平台)来执行这个
此问题已针对其他语言多次提出(尽管大多数答案利用函数 PathRelativePathTo):
- How to get relative path from absolute path
- Getting a file path relative to a particular directory
- How do I get a relative path from one path to another in C#
您在示例中给出了 windows 路径。因此,如果您可以接受使用 WinAPI 函数,则可以使用 PathRelativePathTo.
首先您需要确定文件路径分隔符,如 here 所述。
const char kPathSeparator =
#ifdef _WIN32
'\';
#else
'/';
#endif
然后,您需要编写一个函数来计算规范的绝对文件路径。您将不得不再次使用 #ifdef _WIN32
,因为需要特定的 windows 处理(如果存在 none,则在路径的开头添加当前磁盘)。
之后,删除路径中的所有.
,并删除所有..
及其之前的目录。
写完这个函数后,你需要使用它两次来获取你的起点和目标规范绝对路径,然后正如@Weather Vane 所解释的那样,你需要识别两条路径中的公共部分并添加数字..
连接到目标规范路径的末尾。
这是我能想到的最短的解决方案。
算法其实很简单:
给定 1) 参考路径(结果路径将相对于的路径);和 2) 绝对路径(文件的完整路径):
- 当路径部分相等时:跳过它们
当我们遇到差异时
- 为参考路径的每个剩余部分添加一个“..”
- 从绝对路径添加剩余部分
windows 下的唯一限制是在不同的卷(驱动器号不同)的情况下,在这种情况下我们别无选择,只能 return 原始绝对路径。
跨平台 C 源代码:
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
const char* path_separator = "\";
#else
const char* path_separator = "/";
#endif
#define FILENAME_MAX 1024
char* get_relative_path(char* reference_path, char* absolute_path) {
static char relative_path[FILENAME_MAX];
// init result string
relative_path[0] = '[=10=]';
// check first char (under windows, if differs, we return absolute path)
if(absolute_path[0] != reference_path[0]) {
return absolute_path;
}
// make copies to prevent altering original strings
char* path_a = strdup(absolute_path);
char* path_r = strdup(reference_path);
int inc;
int size_a = strlen(path_a)+1;
int size_r = strlen(path_r)+1;
for(inc = 0; inc < size_a && inc < size_r; inc += strlen(path_a+inc)+1) {
char* token_a = strchr(path_a+inc, path_separator[0]);
char* token_r = strchr(path_r+inc, path_separator[0]);
if(token_a) token_a[0] = '[=10=]';
if(token_r) token_r[0] = '[=10=]';
if(strcmp(path_a+inc, path_r+inc) != 0) break;
}
for(int inc_r = inc; inc_r < size_r; inc_r += strlen(path_r+inc_r)+1) {
strcat(relative_path, "..");
strcat(relative_path, path_separator);
if( !strchr(reference_path+inc_r, path_separator[0]) ) break;
}
if(inc < size_a) strcat(relative_path, absolute_path+inc);
return relative_path;
}