2010-07-14 22 views
10

Tengo un directorio con una gran cantidad de archivos (~ 1mil). Necesito elegir un archivo aleatorio de este directorio. Como hay tantos archivos, os.listdir toma una eternidad para terminar.Elegir un archivo aleatorio de un directorio (con una gran cantidad de archivos) en Python

¿Hay alguna manera de eludir este problema? ¿Quizás de alguna manera conozca la cantidad de archivos en el directorio (sin listarlo) y elija el 'n'to archivo donde n se genera aleatoriamente?

Los archivos en el directorio se nombran aleatoriamente.

+0

¿Qué sistema operativo que se ejecuta? – danben

+0

2.6.30.10.1.amd64-smp # 1 x86_64 GNU/Linux – NoneType

+0

¿Controla los nombres de los archivos en el directorio? – danben

Respuesta

1

No estoy seguro de que esto sea posible. Incluso en el nivel VFS o del sistema de archivos, no hay garantía de que se mantenga un recuento de entradas de directorio. Por ejemplo, muchos sistemas de archivos simplemente registran el tamaño de bytes combinados de las estructuras de entradas de directorio contenidas en un directorio determinado.

Se puede hacer una estimación si las entradas de directorio son estructuras de tamaño fijo, pero esto ahora es poco común (considere LFN para FAT32). Incluso si un sistema de archivos dado proporcionó un conteo de entradas sin necesidad de iterar a través de un directorio, o si el VFS almacenó en caché un registro de una longitud de directorios, estos definitivamente serían sistema operativo, sistema de archivos y núcleo específico.

+0

¿Sería útil si todos los archivos en el directorio son enlaces simbólicos? En mi sistema, todos estos enlaces tienen un tamaño de 512B. Entonces, ¿podríamos posiblemente extraer el número de archivos usando esto y la información de tamaño de directorio combinada? – NoneType

+0

Tengo muchas esperanzas de estar equivocado, estoy ansioso por ver una buena respuesta técnica a su pregunta. –

0

Usted puede ser capaz de obtener esta funcionando:

http://mail.python.org/pipermail/python-list/2009-July/1213182.html

Y eso es probablemente la mejor solución posible a su problema, pero sólo cuando es pequeño n - si va n grande, entonces es probablemente os.listdir tan bueno para su propósito.

He buscado y no he encontrado otra forma de abrir un archivo en un directorio. Si tuviera más tiempo, me inclinaría a jugar un poco y generar mis propios archivos ~ 1mil.


me acaba de ocurrir otra manera de hacer esto: Suponiendo que los archivos son constantes - que no está recibiendo más o menos - se puede mantener una lista de los nombres de archivo en una base de datos SQLite. Entonces sería relativamente simple consultar la base de datos por un nombre al azar ROWID. No sé si todavía estarás plagado por el largo tiempo para buscar el archivo correcto, pero al menos obtener un nombre de archivo debería tomar una pequeña cantidad.

Por supuesto, si los archivos en el directorio se nombran al azar, puede cambiar el nombre de los archivos (?) Y ponerlos en una estructura de directorios como sugiere Adam K.

+0

Probaré la función del generador 'listdir' junto con la heurística de muestreo aleatorio sugerida por Nas Banov. (es decir, muestreo uniforme en todos los nombres de archivo al leerlos uno por uno) – NoneType

0

probar esto, (en este caso es muy rápido con 50K archivos ...)

import glob 
import random 

list = glob.glob("*/*.*") 
print list[random.randrange(0,list.__len__())] 
+0

Esto requiere una cantidad de tiempo igualmente grande. – NoneType

+2

pls note 'random.randrange (0, list .__ len __())' está mejor escrito como 'random.randrange (len (list))' –

3

Por desgracia, creo que no hay una solución a su problema. Uno, no sé de la API portátil que le devolverá el número de entradas en el directorio (sin enumerarlas primero). Dos, no creo que exista una API para devolverle la entrada de directorio por número y no por nombre.

Así que, en general, un programa tendrá que enumerar O (n) entradas de directorio para obtener una sola aleatoria. El enfoque trivial de determinar el número de entradas y luego elegir uno requerirá suficiente RAM para contener la lista completa (os.listdir()) o tendrá que enumerar por segunda vez el directorio para encontrar el elemento aleatorio (n) - operaciones generales n+n/2 en promedio.

Hay una aproximación ligeramente mejor, pero solo levemente, vea randomly-selecting-lines-from-files. En resumen, hay una manera de elegir un elemento aleatorio de la lista/iterador con una longitud desconocida, mientras se lee un artículo a la vez y se asegura de que cualquier elemento se pueda recoger con la misma probabilidad. Pero esto no va a ayudar con os.listdir() porque ya vuelve list en la memoria que ya contiene todas las entradas 1M + - para que pueda preguntar también se acerca len() ...

+0

Esta es una buena idea, estoy tentado de probar esto usando el 'os Función del generador .listdir' que Wayne sugirió. – NoneType

+0

@NoneType: si quieres jugar con él, seguro. Pero no creo que una mejora de solo 2x valga la pena; deberías estar buscando algo lineal o logarítmico. Por eso, aunque debería ser capaz de cambiar el problema de alguna manera ... ¿por qué exactamente necesita hacer esta selección aleatoria de archivos, cuál es la necesidad detrás de esto? ¿Tiene un mejor conocimiento del esquema de nombres de archivos? –

1

que tienen una necesidad similar a la OP.

Creo que adoptaré un método de precaching: almacenas en un archivo .txt la lista de todos los archivos, luego puedes hacer una búsqueda inteligente de una línea aleatoria en tu lista (sin siquiera tener que cargarla) en la memoria), ¡y listo!

Por supuesto, aún debe actualizar la caché, y más importante aún, definir cuando necesita actualizar la caché, pero según sus necesidades, puede ser fácil (justo después de una acción específica, o cuando algo cambió , etc.).

Un código para leer inteligentemente una línea aleatoria de un archivo, en Python, por Jonathan Kupferman:

http://www.regexprn.com/2008/11/read-random-line-in-large-file-in.html

Cuestiones relacionadas