【问题标题】:Find a file in python在python中查找文件
【发布时间】:2010-12-16 00:45:42
【问题描述】:

我有一个文件可能位于每个用户机器上的不同位置。有没有办法实现对文件的搜索?一种可以传递文件名和目录树进行搜索的方法?

【问题讨论】:

标签: python


【解决方案1】:

os.walk 是答案,这将找到第一个匹配项:

import os

def find(name, path):
    for root, dirs, files in os.walk(path):
        if name in files:
            return os.path.join(root, name)

这将找到所有匹配项:

def find_all(name, path):
    result = []
    for root, dirs, files in os.walk(path):
        if name in files:
            result.append(os.path.join(root, name))
    return result

这将匹配一个模式:

import os, fnmatch
def find(pattern, path):
    result = []
    for root, dirs, files in os.walk(path):
        for name in files:
            if fnmatch.fnmatch(name, pattern):
                result.append(os.path.join(root, name))
    return result

find('*.txt', '/path/to/dir')

【讨论】:

  • 请注意,这些示例只会查找文件,而不是同名目录。如果您想在具有该名称的目录中查找 any 对象,您可能需要使用 if name in file or name in dirs
  • 注意区分大小写。 for name in files: 在文件系统中查找 super-photo.jpg 时将失败。 (我生命中的一个小时我想回来 ;-) 有点凌乱的修复是 if str.lower(name) in [x.lower() for x in files]
  • 使用 yield 而不是准备结果列表怎么样? ..... if fnmatch.fnmatch(name, pattern): yield os.path.join(root, name)
  • 请考虑更新您对 Python 3.x 原语的回答
  • 理解列表可以替换函数,例如find_all: res = [os.path.join(root, name) for root, dirs, files in os.walk(path) if name in files]
【解决方案2】:

在 Python 3.4 或更高版本中,您可以使用 pathlib 进行递归通配:

>>> import pathlib
>>> sorted(pathlib.Path('.').glob('**/*.py'))
[PosixPath('build/lib/pathlib.py'),
 PosixPath('docs/conf.py'),
 PosixPath('pathlib.py'),
 PosixPath('setup.py'),
 PosixPath('test_pathlib.py')]

参考:https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob

在 Python 3.5 或更高版本中,您还可以像这样进行递归通配:

>>> import glob
>>> glob.glob('**/*.txt', recursive=True)
['2.txt', 'sub/3.txt']

参考:https://docs.python.org/3/library/glob.html#glob.glob

【讨论】:

    【解决方案3】:

    我使用了os.walk 的一个版本,在一个更大的目录中,时间大约为 3.5 秒。我尝试了两种随机解决方案,但没有太大改善,然后就这样做了:

    paths = [line[2:] for line in subprocess.check_output("find . -iname '*.txt'", shell=True).splitlines()]
    

    虽然它是 POSIX-only,但我得到了 0.25 秒。

    由此,我相信完全有可能以独立于平台的方式优化整个搜索,但这就是我停止研究的地方。

    【讨论】:

      【解决方案4】:

      如果您在 Ubuntu 上使用 Python,并且只希望它在 Ubuntu 上运行,那么一种更快的方法是使用终端的 locate 程序,如下所示。

      import subprocess
      
      def find_files(file_name):
          command = ['locate', file_name]
      
          output = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0]
          output = output.decode()
      
          search_results = output.split('\n')
      
          return search_results
      

      search_results 是绝对文件路径的list。这比上述方法快 10,000 倍,而对于我所做的一次搜索,它快了约 72,000 倍。

      【讨论】:

        【解决方案5】:

        对于快速、独立于操作系统的搜索,请使用scandir

        https://github.com/benhoyt/scandir/#readme

        阅读http://bugs.python.org/issue11406了解详细原因。

        【讨论】:

        • 具体来说,根据@Nadia 的回答使用scandir.walk()。请注意,如果您使用的是 Python 3.5+,os.walk() 已经具有 scandir.walk() 加速。此外,PEP 471 可能是比那个问题更适合阅读信息的文档。
        【解决方案6】:

        如果您使用的是 Python 2,您会遇到由自引用符号链接导致的窗口无限递归问题。

        此脚本将避免遵循这些。请注意,这是特定于 Windows 的

        import os
        from scandir import scandir
        import ctypes
        
        def is_sym_link(path):
            # http://stackoverflow.com/a/35915819
            FILE_ATTRIBUTE_REPARSE_POINT = 0x0400
            return os.path.isdir(path) and (ctypes.windll.kernel32.GetFileAttributesW(unicode(path)) & FILE_ATTRIBUTE_REPARSE_POINT)
        
        def find(base, filenames):
            hits = []
        
            def find_in_dir_subdir(direc):
                content = scandir(direc)
                for entry in content:
                    if entry.name in filenames:
                        hits.append(os.path.join(direc, entry.name))
        
                    elif entry.is_dir() and not is_sym_link(os.path.join(direc, entry.name)):
                        try:
                            find_in_dir_subdir(os.path.join(direc, entry.name))
                        except UnicodeDecodeError:
                            print "Could not resolve " + os.path.join(direc, entry.name)
                            continue
        
            if not os.path.exists(base):
                return
            else:
                find_in_dir_subdir(base)
        
            return hits
        

        它返回一个列表,其中包含指向文件名列表中文件的所有路径。 用法:

        find("C:\\", ["file1.abc", "file2.abc", "file3.abc", "file4.abc", "file5.abc"])
        

        【讨论】:

          【解决方案7】:

          下面我们使用布尔“first”参数在第一个匹配和所有匹配之间切换(默认值相当于“find .-name file”):

          import  os
          
          def find(root, file, first=False):
              for d, subD, f in os.walk(root):
                  if file in f:
                      print("{0} : {1}".format(file, d))
                      if first == True:
                          break 
          

          【讨论】:

            【解决方案8】:

            答案与现有答案非常相似,但略有优化。

            所以你可以通过模式找到任何文件或文件夹:

            def iter_all(pattern, path):
                return (
                    os.path.join(root, entry)
                    for root, dirs, files in os.walk(path)
                    for entry in dirs + files
                    if pattern.match(entry)
                )
            

            通过子字符串:

            def iter_all(substring, path):
                return (
                    os.path.join(root, entry)
                    for root, dirs, files in os.walk(path)
                    for entry in dirs + files
                    if substring in entry
                )
            

            或使用谓词:

            def iter_all(predicate, path):
                return (
                    os.path.join(root, entry)
                    for root, dirs, files in os.walk(path)
                    for entry in dirs + files
                    if predicate(entry)
                )
            

            仅搜索文件或仅搜索文件夹 - 例如,将“dirs + files”替换为仅“dirs”或仅“files”,具体取决于您的需要。

            问候。

            【讨论】:

              【解决方案9】:

              在我从 Ubuntu 20.04 LTS 更新之前,SARose 的回答对我有用。我对他的代码所做的微小改动使它可以在最新的 Ubuntu 版本上运行。

              import subprocess
              
              def find_files(file_name):
                  command = ['locate'+ ' ' + file_name]
                  output = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()[0]
                  output = output.decode()
                  search_results = output.split('\n')
                  return search_results
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2020-11-05
                • 2015-05-22
                • 2013-02-28
                • 1970-01-01
                • 2018-03-28
                • 2019-07-30
                • 1970-01-01
                相关资源
                最近更新 更多