Перечислите файлы в папке как поток, чтобы немедленно начать процесс
Я получаю папку с 1 миллионом файлов.
Я хотел бы начать процесс немедленно, при перечислении файлов в этой папке, в Python или другом языке сценариев.
Обычные функции (os.listdir в python...) блокируются, и моей программе приходится ждать конца списка, что может занять много времени.
Какой лучший способ перечислить огромные папки?
4 ответа
Если это удобно, измените структуру каталогов; но если нет, вы можете использовать ctypes для вызова opendir
а также readdir
,
Вот копия этого кода; все, что я сделал, это сделал отступ правильно, добавьте try/finally
заблокировать и исправить ошибку. Возможно, вам придется отладить его. Особенно структура макета.
Обратите внимание, что этот код не является переносимым. Вам нужно будет использовать разные функции в Windows, и я думаю, что структуры варьируются от Unix к Unix.
#!/usr/bin/python
"""
An equivalent os.listdir but as a generator using ctypes
"""
from ctypes import CDLL, c_char_p, c_int, c_long, c_ushort, c_byte, c_char, Structure, POINTER
from ctypes.util import find_library
class c_dir(Structure):
"""Opaque type for directory entries, corresponds to struct DIR"""
pass
c_dir_p = POINTER(c_dir)
class c_dirent(Structure):
"""Directory entry"""
# FIXME not sure these are the exactly correct types!
_fields_ = (
('d_ino', c_long), # inode number
('d_off', c_long), # offset to the next dirent
('d_reclen', c_ushort), # length of this record
('d_type', c_byte), # type of file; not supported by all file system types
('d_name', c_char * 4096) # filename
)
c_dirent_p = POINTER(c_dirent)
c_lib = CDLL(find_library("c"))
opendir = c_lib.opendir
opendir.argtypes = [c_char_p]
opendir.restype = c_dir_p
# FIXME Should probably use readdir_r here
readdir = c_lib.readdir
readdir.argtypes = [c_dir_p]
readdir.restype = c_dirent_p
closedir = c_lib.closedir
closedir.argtypes = [c_dir_p]
closedir.restype = c_int
def listdir(path):
"""
A generator to return the names of files in the directory passed in
"""
dir_p = opendir(path)
try:
while True:
p = readdir(dir_p)
if not p:
break
name = p.contents.d_name
if name not in (".", ".."):
yield name
finally:
closedir(dir_p)
if __name__ == "__main__":
for name in listdir("."):
print name
Для людей, выходящих из Google, PEP 471 добавил правильное решение в стандартную библиотеку Python 3.5, и он был перенесен на Python 2.6+ и 3.2+ в качестве scandir
модуль на PIP.
Python 3.5+:
os.walk
был обновлен для использования этой инфраструктуры для повышения производительности.os.scandir
возвращает итераторDirEntry
объекты.
Python 2.6 / 2.7 и 3.2/3.3/3.4:
scandir.walk
более производительная версияos.walk
scandir.scandir
возвращает итераторDirEntry
объекты.
scandir()
обертывание итераторов opendir
/readdir
на платформах POSIX и FindFirstFileW
/FindNextFileW
на винде.
Точка возвращения DirEntry
Объекты позволяют кэшировать метаданные, чтобы свести к минимуму количество системных вызовов. (например. DirEntry.stat(follow_symlinks=False)
никогда не делает системный вызов в Windows, потому что FindFirstFileW
а также FindNextFileW
функции добавляют stat
информация бесплатно)
Это кажется грязным, но должно сделать свое дело:
def listdirx(dirname='.', cmd='ls'):
proc = subprocess.Popen([cmd, dirname], stdout=subprocess.PIPE)
filename = proc.stdout.readline()
while filename != '':
yield filename.rstrip('\n')
filename = proc.stdout.readline()
proc.communicate()
Использование: listdirx('/something/with/lots/of/files')
Вот ваш ответ о том, как пройти большой каталог файл за файлом в Windows!
Я искал как маньяк для Windows DLL, которая позволит мне делать то, что делается в Linux, но не повезло.
Итак, я пришел к выводу, что единственный способ - создать свою собственную DLL, которая предоставит мне эти статические функции, но потом я вспомнил pywintypes. И, ДА! это уже сделано там. И даже более того, функция итератора уже реализована! Здорово!
Windows DLL с FindFirstFile(), FindNextFile() и FindClose() все еще может быть где-то там, но я не нашел его. Итак, я использовал pywintypes.
РЕДАКТИРОВАТЬ: они прятались на виду в kernel32.dll. Пожалуйста, смотрите ответ Соколова и мой комментарий к нему.
Извините за зависимость. Но я думаю, что вы можете извлечь win32file.pyd из папки...\site-packages\win32 и возможные зависимости и распространять ее независимо от win32types с вашей программой, если это необходимо.
Я нашел этот вопрос, когда искал, как это сделать, а также некоторые другие.
Вот:
Как скопировать первые 100 файлов из каталога тысяч файлов с помощью Python?
Я разместил полный код с Linux-версией listdir() отсюда (автор Jason Orendorff) и с моей версией для Windows, которую я представляю здесь.
Поэтому, если вы хотите получить более или менее кросс-платформенную версию, зайдите туда или объедините два ответа самостоятельно.
РЕДАКТИРОВАТЬ: Или, еще лучше, используйте модуль scandir или os.scandir() (в Python 3.5) и следующих версиях. Это лучше обрабатывает ошибки и некоторые другие вещи, а также.
from win32file import FindFilesIterator
import os
def listdir (path):
"""
A generator to return the names of files in the directory passed in
"""
if "*" not in path and "?" not in path:
st = os.stat(path) # Raise an error if dir doesn't exist or access is denied to us
# Check if we got a dir or something else!
# Check gotten from stat.py (for fast checking):
if (st.st_mode & 0170000) != 0040000:
e = OSError()
e.errno = 20; e.filename = path; e.strerror = "Not a directory"
raise e
path = path.rstrip("\\/")+"\\*"
# Else: Decide that user knows what she/he is doing
for file in FindFilesIterator(path):
name = file[-2]
# Unfortunately, only drives (eg. C:) don't include "." and ".." in the list:
if name=="." and name=="..": continue
yield name