如何在GNU / Linux上的C / C ++中确定给定相对路径的文件或目录的绝对路径?
我知道realpath()
,但它不适用于不存在的文件。
假设用户输入../non-existant-directory/file.txt
,程序工作目录为/home/user/
我需要的是一个返回/home/non-existant-directory/file.txt
的函数。
我需要此函数来检查给定路径是否在某个子目录中。
答案 0 :(得分:11)
试试realpath
。如果失败,请一次开始从一端删除路径组件,然后重试realpath
直到成功。然后将您删除的组件追加到成功realpath
调用的结果中。
如果您确定包含目录存在且您只想在那里制作文件,则只需删除最多一个组件。
另一种方法是首先创建文件,然后调用realpath
。
答案 1 :(得分:0)
如@R .. GitHub所述,您可以在realpath()
上构建此功能。这是一个示例函数,该函数使用realpath()
确定路径中存在的部分的规范形式,并将路径的不存在部分附加到路径中。
由于realpath()
使用C风格的字符串,因此我决定在这里也使用它们。但是可以很容易地将函数重写为使用std::string
(只是不要忘记将canonical_file_path
复制到std::string
后再释放它!)。
请注意,不会从路径中不存在的部分中删除重复的“ /”条目;它只是简单地附加到确实存在的部分的规范形式上。
////////////////////////////////////////////////////////////////////////////////
// Return the input path in a canonical form. This is achieved by expanding all
// symbolic links, resolving references to "." and "..", and removing duplicate
// "/" characters.
//
// If the file exists, its path is canonicalized and returned. If the file,
// or parts of the containing directory, do not exist, path components are
// removed from the end until an existing path is found. The remainder of the
// path is then appended to the canonical form of the existing path,
// and returned. Consequently, the returned path may not exist. The portion
// of the path which exists, however, is represented in canonical form.
//
// If successful, this function returns a C-string, which needs to be freed by
// the caller using free().
//
// ARGUMENTS:
// file_path
// File path, whose canonical form to return.
//
// RETURNS:
// On success, returns the canonical path to the file, which needs to be freed
// by the caller.
//
// On failure, returns NULL.
////////////////////////////////////////////////////////////////////////////////
char *make_file_name_canonical(char const *file_path)
{
char *canonical_file_path = NULL;
unsigned int file_path_len = strlen(file_path);
if (file_path_len > 0)
{
canonical_file_path = realpath(file_path, NULL);
if (canonical_file_path == NULL && errno == ENOENT)
{
// The file was not found. Back up to a segment which exists,
// and append the remainder of the path to it.
char *file_path_copy = NULL;
if (file_path[0] == '/' ||
(strncmp(file_path, "./", 2) == 0) ||
(strncmp(file_path, "../", 3) == 0))
{
// Absolute path, or path starts with "./" or "../"
file_path_copy = strdup(file_path);
}
else
{
// Relative path
file_path_copy = (char*)malloc(strlen(file_path) + 3);
strcpy(file_path_copy, "./");
strcat(file_path_copy, file_path);
}
// Remove path components from the end, until an existing path is found
for (int char_idx = strlen(file_path_copy) - 1;
char_idx >= 0 && canonical_file_path == NULL;
--char_idx)
{
if (file_path_copy[char_idx] == '/')
{
// Remove the slash character
file_path_copy[char_idx] = '\0';
canonical_file_path = realpath(file_path_copy, NULL);
if (canonical_file_path != NULL)
{
// An existing path was found. Append the remainder of the path
// to a canonical form of the existing path.
char *combined_file_path = (char*)malloc(strlen(canonical_file_path) + strlen(file_path_copy + char_idx + 1) + 2);
strcpy(combined_file_path, canonical_file_path);
strcat(combined_file_path, "/");
strcat(combined_file_path, file_path_copy + char_idx + 1);
free(canonical_file_path);
canonical_file_path = combined_file_path;
}
else
{
// The path segment does not exist. Replace the slash character
// and keep trying by removing the previous path component.
file_path_copy[char_idx] = '/';
}
}
}
free(file_path_copy);
}
}
return canonical_file_path;
}