如何规避Python的谬误';s os.path.commonprefix?
我的问题是找到给定文件集的公共路径前缀 从字面上说,我希望“os.path.commonprefix”能做到这一点。不幸的是,如何规避Python的谬误';s os.path.commonprefix?,python,path,prefix,Python,Path,Prefix,我的问题是找到给定文件集的公共路径前缀 从字面上说,我希望“os.path.commonprefix”能做到这一点。不幸的是,commonprefix位于path中这一事实相当误导,因为它实际上会搜索字符串前缀 我的问题是,如何解决路径的问题?该问题已在中简要提及,但仅作为旁注,拟议的解决方案(在commonprefix的输入中添加斜杠)imho存在问题,因为它将失败,例如: os.path.commonprefix(['/usr/var1/log/', '/usr/var2/log/']) #
commonprefix
位于path
中这一事实相当误导,因为它实际上会搜索字符串前缀
我的问题是,如何解决路径的问题?该问题已在中简要提及,但仅作为旁注,拟议的解决方案(在commonprefix的输入中添加斜杠)imho存在问题,因为它将失败,例如:
os.path.commonprefix(['/usr/var1/log/', '/usr/var2/log/'])
# returns /usr/var but it should be /usr
为了防止其他人落入相同的陷阱,可能值得在另一个问题中讨论此问题:是否有一个简单/可移植的解决方案可以解决此问题,而不依赖于对文件系统的恶意检查(即,访问commonprefix的结果并检查它是否是目录,如果不是,则返回结果的
os.path.dirname
)假设您想要公共目录路径,一种方法是:
os.path.dirname(filename)
获取其目录路径os.path.abspath()
以获取相对于根的路径。(您可能还需要使用os.path.realpath()
来删除符号链接。)os.path.sep
或os.sep
移动找到)os.path.commonprefix()
的结果调用os.path.dirname()
不久前,我遇到了这样一个问题,
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
可以使它更具可移植性。一种可靠的方法是将路径拆分为各个组件,然后找到组件列表中最长的公共前缀
这是一个跨平台的实现,可以很容易地推广到两条以上的路径:
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'
我制作了一个小型python包
commonpath
,用于从列表中查找公共路径
最近的Python版本似乎已经纠正了这个问题。3.5版的新功能是返回公共路径而不是公共字符串前缀的函数。当我考虑这个想法时,我最初拒绝了它,因为我认为在处理相对路径时,添加斜杠是一个问题,比如空白的f当前目录中的文件名。但是,当将所有路径包装到
os.path.abspath
中时,即使是相对路径和绝对路径的混合也不会有问题,对吗?@bluenote10 right,abspath
而不是normpath
将处理相对路径。不确定是否为空文件名,因为这与dupl不明确icated路径分隔符。有人允许零长度的文件名吗?哦,我不是指空文件名,只是“aSimpleFileName”
。应该提到的是,这完全取决于路径是文件路径还是目录路径(这必须是一种惯例,以防我们想要避免文件系统访问)在我的问题中,路径实际上是文件而不是目录。我认为在这种情况下,正确的顺序是:(a)转换为abspath以处理相对/绝对路径的混合;(b)应用dirname将其转换为正确的目录。从这一点上,我认为我们可以安全地应用您的解决方案。哦,现在我明白了,这一点很好。您需要提前知道您的字符串是文件还是目录路径。我以为我已经解决了这个问题,但我只是假设了目录。我接受了这个答案,因为它在主目录下相当健壮非常简洁。有趣的是,与基于os.path.compomatiprefix
(参见Dan Getz的回答)的解决方案相比,组件级比较并不取决于输入路径是文件名还是目录名(仅仅因为文件名是唯一的组件)。对于更稳健的方法,我推荐EOL的答案,并进一步了解os.path.commonprefix
Dan Getz的问题。Dan Getz的答案非常有启发性。谢谢大家!这给出了一个文件名或目录名的合理答案,但在一般情况下,您无法确定函数的结果是文件还是目录命名而不做更多的工作(例如通过控制输入)。此外,我相信这会返回与os.path.dirname(os.path.commonprefix([p+'/'表示l中的p])完全相同的结果
?@DanGetz显然不一样。我只是在windows上用os.path.sep
替换了它,@DanD提供的代码得到了正确的公共路径,而您的代码段返回None
。@这是什么输入导致的?我不知道dirname
可以返回None
@DanGetz I must在某个地方把它搞砸了。我刚刚在一个新的环境中再次尝试了它,它成功了。我的坏。相关问题:。管道中有一个补丁。谢谢!这是切换到python 3的另一个原因——很高兴我去年终于这么做了。
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'