2010-12-09 4 views
8

Obtengo una carpeta con 1 millón de archivos en ella.Lista de archivos en una carpeta como una secuencia para comenzar el proceso inmediatamente

Me gustaría comenzar el proceso inmediatamente, al listar los archivos en esta carpeta, en Python u otro idioma de scripts.

Las funciones habituales (os.listdir en python ...) están bloqueando y mi programa tiene que esperar al final de la lista, lo que puede llevar mucho tiempo.

¿Cuál es la mejor manera de enumerar grandes carpetas?

+0

¿Quieres la funciones POSIX opendir/readdir, creo, pero yo don' Creo que están disponibles en la biblioteca estándar de Python. ¿Cuál es el procesamiento que planea hacer en los nombres de archivo? –

Respuesta

12

Si es conveniente, cambie la estructura de su directorio; pero si no, puede use ctypes to call opendir and readdir.

Aquí hay una copia de ese código; todo lo que hice fue sangrarlo apropiadamente, agregar el bloque try/finally y corregir un error. Es posible que deba depurarlo. Particularmente el diseño de la estructura.

Tenga en cuenta que este código es no portátil. Debería usar diferentes funciones en Windows, y creo que las estructuras varían de Unix a 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 
+0

¿Dónde se define "c_dir_p"? – jldupont

+0

Bastante seguro de que esto falta 'c_dir_p = POINTER (c_dir)' – jdi

+0

Vaya. Tienes razón, lo agregaré. –

3

Esto se siente sucio, pero debe hacer el truco:

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() 

Uso: listdirx('/something/with/lots/of/files')

+0

Eso es genial – Thomas

+0

'ls' ordena los nombres de los archivos, al menos por defecto. Así que no creo que pueda comenzar a devolverlos más rápido de lo que 'os.listdir()' podría. ¿Hay una bandera para hacer que 'ls' no esté ordenada? –

+2

'ls -f' no ordena. Tenga en cuenta que '-f' enciende el indicador '-a', por lo que si no quiere archivos ocultos, directorios ocultos,' .' y '..' tendrían que ser filtrados. –

0

Aquí está su respuesta sobre cómo atravesar un archivo de directorio grande por el archivo en Windows!

Busqué como un maníaco una DLL de Windows que me permita hacer lo que se hace en Linux, pero no tuve suerte.

Entonces, llegué a la conclusión de que la única manera es crear mi propia DLL que me exponga esas funciones estáticas, pero luego recordé los tipos de pywin. Y, YEEY! esto ya está hecho allí. ¡Y aún más, una función de iterador ya está implementada! ¡Guay!

Una DLL de Windows con FindFirstFile(), FindNextFile() y FindClose() puede estar todavía en algún lugar pero no la encontré. Entonces, utilicé los tipos de piroelectrones.

EDITAR: Estaban escondidos a plena vista en kernel32.dll. Por favor, mira la respuesta de ssokolow y mi comentario al respecto.

Lo siento por la dependencia. Pero creo que puede extraer win32file.pyd de la carpeta ... \ site-packages \ win32 y dependencias eventuales y distribuirlo independientemente de win32types con su programa si es necesario.

Encontré esta pregunta al buscar cómo hacer esto, y algunos otros también.

aquí:

How to copy first 100 files from a directory of thousands of files using python?

he publicado un código completo de la versión para Linux de listdir() de aquí (por Jason Orendorff) y con mi versión de Windows que presento aquí.

Cualquiera que desee una versión más o menos multiplataforma, vaya allí o combine dos respuestas usted mismo.

EDITAR: O mejor aún, use el módulo scandir o os.scandir() (en Python 3.5) y siguientes versiones. Es mejor que maneje los errores y algunas otras cosas también.

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 
2

Para las personas que vienen de fuera de Google, PEP 471 añadió una solución apropiada en la biblioteca estándar de Python 3.5 y se puso portado a Python 2.6+ y 3.2+ como el módulo scandir de PIP.

Fuente: https://stackoverflow.com/a/34922054/435253

Python 3.5 +:

  • os.walk se ha actualizado para utilizar esta infraestructura para un mejor rendimiento.
  • os.scandir devuelve un iterador sobre DirEntry objetos.

Python 2.6/2.7 y 3.2/3.3/3.4:

  • scandir.walk es una versión más performant de os.walk
  • scandir.scandir devuelve un iterador sobre DirEntry objetos.

Los iteradores scandir() envuelven opendir/readdir en plataformas POSIX y FindFirstFileW/FindNextFileW en Windows.

El objetivo de devolver objetos DirEntry es permitir que los metadatos se guarden en caché para minimizar el número de llamadas al sistema realizadas. (. Ej DirEntry.stat(follow_symlinks=False) nunca se hace una llamada al sistema de Windows, ya que las funciones FindFirstFileW y FindNextFileW tirar stat información de forma gratuita)

Fuente: https://docs.python.org/3/library/os.html#os.scandir

+0

Parece que su desarrollo comenzó en 2013. pero no lo encontré en ese momento (2015). Realmente no sé cómo. Por lo tanto, escribí mi propia solución y también me decepcionó descubrir (en el código de Scandir) que FindFirstFile está en kernel32.dll. Todo el tiempo escondido frente a mi nariz. Por ambas razones, estaba a punto de ahogarme en una cucharilla, pero decidí editar mis publicaciones. : D ¡Has llegado primero, entonces +1! OK, todavía puedo agregar información sobre FindFirstFile(). :RE – Dalen

Cuestiones relacionadas