在 Windows 中查找相对于另一个的路径

发布于 2024-08-09 10:37:57 字数 2186 浏览 1 评论 0原文

这个问题应该是显而易见的,但我还没有能够解决它。

我需要一个函数,它接受两个参数,每个参数一个文件路径,相对或绝对,并返回一个文件路径,该文件路径是相对于第二个路径(开始)解析的第一个路径(目标)。解析的路径可能是相对于当前目录的,也可能是绝对的(我不在乎)。

这里作为一个尝试的实现,完成了几个文档测试,练习了一些示例用例(并演示了失败的地方)。 我的源代码存储库中还提供了可运行的脚本,但可能会改变。如果未提供参数,可运行脚本将运行 doctest;如果提供,则将向 findpath 传递一两个参数。

def findpath(target, start=os.path.curdir):
    r"""
    Find a path from start to target where target is relative to start.

    >>> orig_wd = os.getcwd()
    >>> os.chdir('c:\\windows') # so we know what the working directory is

    >>> findpath('d:\\')
    'd:\\'

    >>> findpath('d:\\', 'c:\\windows')
    'd:\\'

    >>> findpath('\\bar', 'd:\\')
    'd:\\bar'

    >>> findpath('\\bar', 'd:\\foo') # fails with '\\bar'
    'd:\\bar'

    >>> findpath('bar', 'd:\\foo')
    'd:\\foo\\bar'

    >>> findpath('bar\\baz', 'd:\\foo')
    'd:\\foo\\bar\\baz'

    >>> findpath('\\baz', 'd:\\foo\\bar') # fails with '\\baz'
    'd:\\baz'

    Since we're on the C drive, findpath may be allowed to return
    relative paths for targets on the same drive. I use abspath to
    confirm that the ultimate target is what we expect.
    >>> os.path.abspath(findpath('\\bar'))
    'c:\\bar'

    >>> os.path.abspath(findpath('bar'))
    'c:\\windows\\bar'

    >>> findpath('..', 'd:\\foo\\bar')
    'd:\\foo'

    >>> findpath('..\\bar', 'd:\\foo')
    'd:\\bar'

    The parent of the root directory is the root directory.
    >>> findpath('..', 'd:\\')
    'd:\\'

    restore the original working directory
    >>> os.chdir(orig_wd)
    """
    return os.path.normpath(os.path.join(start, target))

从 doctest 中的注释可以看出,当起始指定驱动器号并且目标相对于驱动器的根目录时,此实现会失败。

这带来了一些问题:

  1. 这种行为是 os.path.join 的限制吗?换句话说, os.path.join('d:\foo', '\bar') 应该解析为 'd:\bar' 吗?作为一名 Windows 用户,我倾向于这么认为,但我讨厌认为像 path.join 这样的成熟函数需要进行修改才能处理这个用例。
  2. 是否有现有目标路径解析器(例如 findpath)的示例可以在所有这些测试用例中工作?
  3. 如果上述问题的答案为“否”,您将如何实现这一期望的行为?

This problem should be a no-brainer, but I haven't yet been able to nail it.

I need a function that takes two parameters, each a file path, relative or absolute, and returns a filepath which is the first path (target) resolved relative to the second path (start). The resolved path may be relative to the current directory or may be absolute (I don't care).

Here as an attempted implementation, complete with several doc tests, that exercises some sample uses cases (and demonstrates where it fails). A runnable script is also available on my source code repository, but it may change. The runnable script will run the doctest if no parameters are supplied or will pass one or two parameters to findpath if supplied.

def findpath(target, start=os.path.curdir):
    r"""
    Find a path from start to target where target is relative to start.

    >>> orig_wd = os.getcwd()
    >>> os.chdir('c:\\windows') # so we know what the working directory is

    >>> findpath('d:\\')
    'd:\\'

    >>> findpath('d:\\', 'c:\\windows')
    'd:\\'

    >>> findpath('\\bar', 'd:\\')
    'd:\\bar'

    >>> findpath('\\bar', 'd:\\foo') # fails with '\\bar'
    'd:\\bar'

    >>> findpath('bar', 'd:\\foo')
    'd:\\foo\\bar'

    >>> findpath('bar\\baz', 'd:\\foo')
    'd:\\foo\\bar\\baz'

    >>> findpath('\\baz', 'd:\\foo\\bar') # fails with '\\baz'
    'd:\\baz'

    Since we're on the C drive, findpath may be allowed to return
    relative paths for targets on the same drive. I use abspath to
    confirm that the ultimate target is what we expect.
    >>> os.path.abspath(findpath('\\bar'))
    'c:\\bar'

    >>> os.path.abspath(findpath('bar'))
    'c:\\windows\\bar'

    >>> findpath('..', 'd:\\foo\\bar')
    'd:\\foo'

    >>> findpath('..\\bar', 'd:\\foo')
    'd:\\bar'

    The parent of the root directory is the root directory.
    >>> findpath('..', 'd:\\')
    'd:\\'

    restore the original working directory
    >>> os.chdir(orig_wd)
    """
    return os.path.normpath(os.path.join(start, target))

As you can see from the comments in the doctest, this implementation fails when the start specifies a drive letter and the target is relative to the root of the drive.

This brings up a few questions

  1. Is this behavior a limitation of os.path.join? In other words, should os.path.join('d:\foo', '\bar') resolve to 'd:\bar'? As a Windows user, I tend to think so, but I hate to think that a mature function like path.join would need alteration to handle this use case.
  2. Is there an example of an existing target path resolver such as findpath that will work in all of these test cases?
  3. If 'no' to the above questions, how would you implement this desired behavior?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

零崎曲识 2024-08-16 10:37:57

我同意你的观点:这似乎是 os.path.join 的缺陷。看来您必须单独处理驱动器。这段代码通过了您的所有测试:(

def findpath(target, start=os.path.curdir):
    sdrive, start = os.path.splitdrive(start)
    tdrive, target = os.path.splitdrive(target)
    rdrive = tdrive or sdrive
    return os.path.normpath(os.path.join(rdrive, os.path.join(start, target)))

是的,我必须嵌套两个 os.path.join 才能使其正常工作......)

I agree with you: this seems like a deficiency in os.path.join. Looks like you have to deal with the drives separately. This code passes all your tests:

def findpath(target, start=os.path.curdir):
    sdrive, start = os.path.splitdrive(start)
    tdrive, target = os.path.splitdrive(target)
    rdrive = tdrive or sdrive
    return os.path.normpath(os.path.join(rdrive, os.path.join(start, target)))

(and yes, I had to nest two os.path.join's to get it to work...)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文