【问题标题】:Removing the first folder in a path删除路径中的第一个文件夹
【发布时间】:2014-12-30 16:50:10
【问题描述】:

我有一条看起来像这样的路径

/First/Second/Third/Fourth/Fifth

我想从中删除First,从而获得

Second/Third/Fourth/Fifth

我能想出的唯一想法是递归使用os.path.split,但这似乎不是最优的。有没有更好的解决方案?

【问题讨论】:

    标签: python path


    【解决方案1】:

    os.path 模块中确实没有任何东西可以做到这一点。每隔一段时间,就会有人建议创建一个 splitall 函数来返回所有组件的列表(或迭代器),但它从未获得足够的关注。

    部分原因是每次有人建议向 os.path 添加新功能时,它都会重新点燃长期以来对库总体设计的不满,导致有人提出新的、更类似于 OO 的 API对于不推荐使用的操作系统,笨重的 API 的路径。在 3.4 中,这终于发生了,pathlib。它已经拥有os.path 中没有的功能。所以:

    >>> import pathlib
    >>> p = pathlib.Path('/First/Second/Third/Fourth/Fifth')
    >>> p.parts[2:]
    ('Third', 'Fourth', 'Fifth')
    >>> pathlib.Path(*p.parts[2:])
    PosixPath('Second/Third/Fourth/Fifth')
    

    或者……您确定要删除第一个组件,而不是这样做?

    >>> p.relative_to(*p.parts[:2])
    PosixPath('Second/Third/Fourth/Fifth')
    

    如果您需要在 2.6-2.7 或 3.2-3.3 中执行此操作,有一个 backport of pathlib

    当然,您可以使用字符串操作,只要您小心地规范化路径并使用os.path.sep,并确保您使用非绝对路径或带有驱动器号的系统处理繁琐的细节,还有……

    或者你也可以结束你的递归os.path.split。一旦你把它包起来,究竟什么是“非最佳”的?它可能会慢一点,但我们在这里谈论的是纳秒,甚至比在文件上调用 stat 快很多数量级。如果你有一个深度为 1000 个目录的文件系统,它会出现递归深度问题,但你见过吗? (如果是这样,你总是可以把它变成一个循环……) 把它包起来并编写好的单元测试需要几分钟,但这是你只需做一次就再也不用担心的事情了。所以,老实说,如果你不想使用pathlib,我会这样做。

    【讨论】:

    • 在性能方面你是完全正确的:我们谈论的是纳秒;更多的是我试图学习最好的方法/其他方法
    • @Hackaholic:正如答案详细解释的那样,pathlib 带有 Python 3.4+,您可以安装 2.6-2.7 或 3.2-3.3 的 backport。
    • @meto:是的,这完全合理。只是当人们说“优化”或“高效”时,他们实际上是在询问(时间)性能,在实际上并不重要的情况下,所以最好确定人们要求的是什么……
    • @abarnert 作为一个严肃的问题:在您看来,问这样的问题的最佳方式是什么?
    • @meto:在 Python 中,通常询问什么是“最 Pythonic”是一个不错的选择——人们会将其解释为不仅是“Python 中最惯用的”,而且是“最简单的”和“全能的”最好的”。
    【解决方案2】:

    有点像另一个答案,利用 os.path :

    os.path.join(*(x.split(os.path.sep)[2:]))
    

    ...假设您的字符串以分隔符开头。

    【讨论】:

    • 你能解释一下这里“*”的使用吗?
    • @Luke * 用于将(x.split(os.path.sep)[2:]) 生成的集合视为*args 关键字。但是,如果路径太短,这将不起作用,因为参数列表将完全为空
    【解决方案3】:

    一个简单的方法

    a = '/First/Second/Third/Fourth/Fifth'
    "/".join(a.strip("/").split('/')[1:])
    

    输出:

    Second/Third/Fourth/Fifth
    

    在上面的代码中,我已经拆分了字符串。然后加入离开第一个元素

    使用itertools.dropwhile

    >>> a = '/First/Second/Third/Fourth/Fifth'
    >>> "".join(list(itertools.dropwhile(str.isalnum, a.strip("/"))[1:])
    'Second/Third/Fourth/Fifth'
    

    【讨论】:

    • 起初我认为这不适用于以路径分隔符开头的路径,因为您似乎肆意从字符串中删除第一个字符,但经过进一步审查,如果第一个字符有什么关系您只是删除了第一段。 +1,但也许答案中的某些内容会说明这一点(或者可能是您帮助过的人的评论)
    • @iLoveTux 让它更高效
    【解决方案4】:

    你可以试试:

    os.path.relpath(your_path, '/First')
    

    【讨论】:

      【解决方案5】:

      我一直在寻找是否有本地方法可以做到这一点,但似乎没有。

      我知道这个话题很老了,但这是我为找到最佳解决方案所做的: 基本上有两种方法:使用 split() 和使用 len()。两者都必须使用切片。

      1) 使用 split()

      import time
      
      start_time = time.time()
      
      path = "/folder1/folder2/folder3/file.zip"
      for i in xrange(500000):
          new_path = "/" + "/".join(path.split("/")[2:])
      
      print("--- %s seconds ---" % (time.time() - start_time))
      

      结果:--- 0.420122861862 秒 ---

      *删除 new_path = "/" + "/".... 行中的字符 "/" 并没有太大提高性能。

      2) 使用 len()。 此方法仅在您提供要删除的文件夹时才有效

      import time
      
      start_time = time.time()
      
      path = "/folder1/folder2/folder3/file.zip"
      folder = "/folder1"
      for i in xrange(500000):
          if path.startswith(folder):
              a = path[len(folder):]
      
      print("--- %s seconds ---" % (time.time() - start_time))
      

      结果:--- 0.199596166611 秒 ---

      *即使使用“if”来检查路径是否以文件名开头,它也比第一种方法快两倍。

      总而言之:每种方法都有优缺点。如果您完全确定要删除的文件夹使用方法二,否则我建议使用这里的人之前提到的方法 1。

      【讨论】:

      • path.split(os.path.sep,2)[ 2 ] 如果有前导分隔符,或者 path.split(os.path.sep,1)[ 1 ] 如果有不是吗?
      猜你喜欢
      • 2014-12-11
      • 2011-07-15
      • 2021-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多