我的问题是找到给定文件集的公共路径前缀。
从字面上看,我期待“os.path.commonprefix”可以做到这一点。不幸的是,commonprefix
位于path
的事实相当具有误导性,因为它实际上会搜索字符串前缀。
对我来说,问题是如何才能真正解决路径?在this (fairly high rated) answer中简要提到了这个问题,但只是作为附注并且提议的解决方案(对commonprefix的输入附加斜线)imho存在问题,因为它将失败例如:
os.path.commonprefix(['/usr/var1/log/', '/usr/var2/log/'])
# returns /usr/var but it should be /usr
为了防止其他人陷入同一陷阱,可能值得在一个单独的问题中讨论这个问题:是否有一个简单/可移植的解决方案,不依赖于对文件系统的讨厌检查(即,访问commonprefix的结果并检查它是否是一个目录,如果没有,则返回结果的os.path.dirname
?
答案 0 :(得分:11)
前一段时间我碰到了os.path.commonprefix
是一个字符串前缀,而不是预期的路径前缀。所以我写了以下内容:
def commonprefix(l):
# this unlike the os.path.commonprefix version
# always returns path prefixes as it compares
# path component wise
cp = []
ls = [p.split('/') for p in l]
ml = min( len(p) for p in ls )
for i in range(ml):
s = set( p[i] for p in ls )
if len(s) != 1:
break
cp.append(s.pop())
return '/'.join(cp)
可以通过将'/'
替换为os.path.sep
来使其更具可移植性。
答案 1 :(得分:10)
似乎在最近的Python版本中已经纠正了这个问题。版本3.5中的新功能是函数os.path.commonpath()
,它返回公共路径而不是公共字符串前缀。
答案 2 :(得分:6)
假设您需要公共目录路径,一种方法是:
os.path.dirname(filename)
以获取其目录路径。os.path.abspath( )
来获取相对于根的路径。 (您可能还想使用os.path.realpath( )
删除符号链接。)os.path.sep
或os.sep
移植)。os.path.dirname( )
。os.path.commonprefix( )
醇>
在代码中(不删除符号链接):
def common_path(directories):
norm_paths = [os.path.abspath(p) + os.path.sep for p in directories]
return os.path.dirname(os.path.commonprefix(norm_paths))
def common_path_of_filenames(filenames):
return common_path([os.path.dirname(f) for f in filenames])
答案 3 :(得分:2)
一种强大的方法是将路径拆分为单个组件,然后找到组件列表中最长的公共前缀。
这是一个跨平台的实现,可以很容易地推广到两个以上的路径:
import os.path
import itertools
def components(path):
'''
Returns the individual components of the given file path
string (for the local operating system).
The returned components, when joined with os.path.join(), point to
the same location as the original path.
'''
components = []
# The loop guarantees that the returned components can be
# os.path.joined with the path separator and point to the same
# location:
while True:
(new_path, tail) = os.path.split(path) # Works on any platform
components.append(tail)
if new_path == path: # Root (including drive, on Windows) reached
break
path = new_path
components.append(new_path)
components.reverse() # First component first
return components
def longest_prefix(iter0, iter1):
'''
Returns the longest common prefix of the given two iterables.
'''
longest_prefix = []
for (elmt0, elmt1) in itertools.izip(iter0, iter1):
if elmt0 != elmt1:
break
longest_prefix.append(elmt0)
return longest_prefix
def common_prefix_path(path0, path1):
return os.path.join(*longest_prefix(components(path0), components(path1)))
# For Unix:
assert common_prefix_path('/', '/usr') == '/'
assert common_prefix_path('/usr/var1/log/', '/usr/var2/log/') == '/usr'
assert common_prefix_path('/usr/var/log1/', '/usr/var/log2/') == '/usr/var'
assert common_prefix_path('/usr/var/log', '/usr/var/log2') == '/usr/var'
assert common_prefix_path('/usr/var/log', '/usr/var/log') == '/usr/var/log'
# Only for Windows:
# assert common_prefix_path(r'C:\Programs\Me', r'C:\Programs') == r'C:\Programs'
答案 4 :(得分:2)
我已经创建了一个小的python包commonpath
来查找列表中的常用路径。附带一些不错的选择。