【问题标题】:How to get absolute path of file or directory, that does *not* exist?如何获取*不存在的文件或目录的绝对路径?
【发布时间】:2012-06-14 13:22:20
【问题描述】:

如何在 GNU/Linux 上从 C/C++ 中的给定相对路径确定文件或目录的绝对路径?
我知道realpath(),但它不适用于不存在的文件。

假设用户输入../non-existant-directory/file.txt,程序工作目录为/home/user/
我需要的是一个返回/home/non-existant-directory/file.txt的函数。

我需要这个函数来检查给定的路径是否在某个子目录中。

【问题讨论】:

  • 我不认为这样的东西是内置的。你将不得不自己编写代码。

标签: c file file-io relative-path absolute-path


【解决方案1】:

试试realpath。如果失败,则开始一次从末尾删除路径组件并重试realpath,直到成功。然后将您删除的组件附加到成功的realpath 调用的结果中。

如果您确定包含目录存在并且只想在其中创建文件,则最多只需要删除一个组件。

另一种方法是先创建文件,然后调用realpath

【讨论】:

  • 如果文件名不存在的部分中有...s,这将不起作用。例如。 /foo/bar/this_dir_doesn't_exist/../../baz 应标准化为 /foo/baz 但使用您的方法它将保持不变。我认为您需要先解决...
  • @Timmmm:在调用realpath(或自己重新实现)之前无法解决它们,因为您不知道.. 之前的组件是否是符号链接。如果它们可能存在,您只需要按照我的答案中的过程在尾部 after 中应用 .. 组件。
  • 嗯,很好——所以,按照你的方法,然后解决..。我希望 Linux 对此有一个系统调用。
【解决方案2】:

正如@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;
}

【讨论】:

  • 这不适用于路径的虚构部分中的多个../。理想情况下,无论路径是否存在,我都希望摆脱字符串中的所有./../。 Windows 上的_fullpath() 做到了。我不太了解realpath() 在不存在的路径上失败的用例。您可以使用stat() 检查路径。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 2012-04-06
  • 1970-01-01
  • 2016-01-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多