Перечислите файлы в папке как поток, чтобы немедленно начать процесс

Я получаю папку с 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.

Источник: /questions/16724356/luchshij-sposob-poluchit-spisok-fajlov-bolshoj-direktorii-na-python/16724385#16724385

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 информация бесплатно)

Источник: https://docs.python.org/3/library/os.html

Это кажется грязным, но должно сделать свое дело:

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
Другие вопросы по тегам