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

这个问题应该是一个不费吹灰之力,但我还没有能够钉住它。

我需要一个带有两个参数的函数,每个参数都是相对或绝对的文件path,并返回相对于第二个path(开始)parsing的第一个path(目标)的文件path。 解决的path可能是相对于当前目录或可能是绝对的(我不在乎)。

在这里,作为一个尝试的实现,完成了几个文档testing,它将演示一些示例用例(并演示它失败的地方)。 我的源代码库上也有一个可运行的脚本 ,但它可能会改变。 如果没有提供参数,可运行脚本将运行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')parsing为'd:\ bar'? 作为一个Windows用户,我倾向于这样认为,但是我讨厌认为像path.join这样成熟的函数需要改变来处理这个用例。
  2. 有没有一个现有的目标pathparsing器的例子,如findpath,将在所有这些testing用例中工作?
  3. 如果对上述问题“不”,你将如何实现这个理想的行为?

我同意你的看法:这似乎是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来使它工作…)