在 python 中查找文件
- 2025-01-15 08:46:00
- admin 原创
- 133
问题描述:
我有一个文件,它可能位于每个用户机器的不同位置。有没有办法实现对文件的搜索?有没有办法可以传递文件的名称和目录树进行搜索?
解决方案 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')
解决方案 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,但我得到了 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('
')
return search_results
search_results
是list
绝对文件路径。这比上述方法快 10,000 倍,对于我进行的一次搜索,它快了约 72,000 倍。
解决方案 5:
如果您正在使用 Python 2,那么您会遇到由自引用符号链接导致的 Windows 无限递归问题。
此脚本将避免遵循这些。请注意,这是特定于 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"])
解决方案 6:
下面我们使用布尔“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
解决方案 7:
该答案与现有的答案非常相似,但略微进行了优化。
因此,您可以按照模式找到任何文件或文件夹:
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”,具体取决于您的需要。
问候。
解决方案 8:
SARose 的答案对我来说一直有效,直到我从 Ubuntu 20.04 LTS 更新。我对他的代码做了一些小改动,使它可以在最新的 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('
')
return search_results
解决方案 9:
@FMF 的答案在这个版本中存在一些问题,因此我做了一些调整以使其正常工作。
import os
from os 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(str(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
except PermissionError:
print("Skipped " + os.path.join(direc, entry.name) + ". I lacked permission to navigate")
continue
if not os.path.exists(base):
return
else:
find_in_dir_subdir(base)
return hits
unicode() 在 Python 3 中已更改为 str(),因此我进行了调整(第 8 行)
我还添加了(第 25 行)PermissionError 异常。这样,如果程序发现无法访问的文件夹,它就不会停止。
最后,我想给出一点警告。运行程序时,即使你正在寻找单个文件/目录,也请确保将其作为列表传递。否则,你会得到很多不一定与你的搜索相匹配的答案。
使用示例:
查找(“C:\”,[“Python”,“家庭作业”])
或者
查找(“C:\\”,[“家庭作业”])
但是,例如: find("C:\\", "Homework") 将给出不想要的答案。
如果我说我知道为什么会发生这种情况,那我就是在撒谎。再说一遍,这不是我的代码,我只是做了必要的调整让它工作。所有功劳都应归功于@FMF
扫码咨询,免费领取项目管理大礼包!