2010-02-01 18 views
6

Tengo una carpeta con 100k archivos de texto. Quiero poner archivos con más de 20 líneas en otra carpeta. ¿Cómo hago esto en Python? Usé os.listdir, pero por supuesto, no hay suficiente memoria para cargar los nombres de archivos en la memoria. ¿Hay alguna manera de obtener tal vez 100 nombres de archivo a la vez?Filtrar archivos en una carpeta muy grande

Aquí está mi código:

import os 
import shutil 

dir = '/somedir/' 

def file_len(fname): 
    f = open(fname,'r') 
    for i, l in enumerate(f): 
     pass 
    f.close() 
    return i + 1 

filenames = os.listdir(dir+'labels/') 

i = 0 
for filename in filenames: 
    flen = file_len(dir+'labels/'+filename) 
    print flen 
    if flen > 15: 
     i = i+1 
     shutil.copyfile(dir+'originals/'+filename[:-5], dir+'filteredOrigs/'+filename[:-5]) 
print i 

y de salida:

Traceback (most recent call last): 
    File "filterimage.py", line 13, in <module> 
    filenames = os.listdir(dir+'labels/') 
OSError: [Errno 12] Cannot allocate memory: '/somedir/' 

del script modificado Aquí:

import os 
import shutil 
import glob 

topdir = '/somedir' 

def filelen(fname, many): 
    f = open(fname,'r') 
    for i, l in enumerate(f): 
     if i > many: 
      f.close() 
      return True 
    f.close() 
    return False 

path = os.path.join(topdir, 'labels', '*') 
i=0 
for filename in glob.iglob(path): 
    print filename 
    if filelen(filename,5): 
     i += 1 
print i 

funciona en una carpeta con un menor número de archivos, pero con la mayor carpeta, todo lo que imprime es "0" ... Funciona en el servidor Linux, imprime 0 en mac ... oh bien ...

+3

"no hay suficiente memoria para cargar los nombres de archivos en la memoria" ¿De verdad? 100K nombres de archivo no es realmente tanta memoria. ¿Qué error estás recibiendo? ¿Puedes publicar el fragmento de código? –

+1

¿Por qué la memoria es un problema? 100k archivos con nombres de, digamos, 10 caracteres cada uno, son 10^7 bytes = 10 megabytes, realmente no demasiado grandes. –

+0

Estoy de acuerdo en que un OOM es extraño. ¿Qué sucede si ingresas 'filenames = os.listdir ('/ somedir/labels /')' en REPL? –

Respuesta

4

puede intentar utilizar glob.iglob que devuelve un iterador:

topdir = os.path.join('/somedir', 'labels', '*') 
for filename in glob.iglob(topdir): 
    if filelen(filename) > 15: 
      #do stuff 

También, por favor no utilice dir de un nombre de variable: que estés sombreando el built-in.

Otra mejora importante que puede introducir es su función filelen. Si lo reemplaza con lo siguiente, ahorrará mucho tiempo. Confía en mí, what you have now is the slowest alternative:

def many_line(fname, many=15): 
    for i, line in enumerate(open(fname)): 
     if i > many: 
      return True 
    return False 
+0

¿Alguien ha leído la función 'many_line' antes de presionar el botón upvote ??? –

+0

@John: ¿alguien aquí puede distinguir el error tipográfico del verdadero problema? – SilentGhost

+0

+1 gordo error de tipografía del año –

0
import os,shutil 
os.chdir("/mydir/") 
numlines=20 
destination = os.path.join("/destination","dir1") 
for file in os.listdir("."): 
    if os.path.isfile(file): 
     flag=0 
     for n,line in enumerate(open(file)): 
      if n > numlines: 
       flag=1 
       break 
     if flag: 
      try: 
       shutil.move(file,destination) 
      except Exception,e: print e 
      else: 
       print "%s moved to %s" %(file,destination) 
+0

Esa es la tarea básica que cseric está tratando de lograr, pero no es una respuesta a su pregunta. – jcdyer

+0

sí lo es. Preguntó cómo poner archivos con más de 20 líneas en otra carpeta usando Python. – ghostdog74

+2

No, él preguntó cómo hacerlo para un directorio que tenía 100.000 archivos, y señaló que llamar a os.listdir ("."), Como lo hace, significa que se queda sin memoria. –

2

Un par de pensamientos. Primero, puede usar el módulo glob para obtener grupos de archivos más pequeños. En segundo lugar, ordenar por recuento de líneas va a consumir mucho tiempo, ya que debe abrir cada archivo y contar líneas. Si puede dividir por conteo de bytes, puede evitar abrir los archivos utilizando el módulo stat. Si es crucial que la división ocurra en 20 líneas, al menos puede cortar grandes franjas de archivos al calcular un número mínimo de caracteres que tendrá un archivo de 20 líneas de su tipo, y no abrir ningún archivo más pequeño que eso.

0

cómo sobre el uso de un script de shell? usted podría escoger un archivo a la vez:

for f in `ls`; 
loop 
if `wc -l f`>20; then 
    mv f newfolder 
fi 
end loop 

PPL favor corrija si estoy equivocado en cualquier forma

+1

no use ls con for loop así. Se rompe en los archivos con espacios .use shell expansion. – ghostdog74

0

La respuesta actualmente aceptado simplemente no funciona. Esta función:

def many_line(fname, many=15): 
    for i, line in enumerate(line): 
     if i > many: 
      return True 
    return False 

tiene dos problemas: En primer lugar, el arg fname no se utiliza y el archivo no se abre. En segundo lugar, la llamada al enumerate(line) fallará porque line no está definido.

Cambiando enumerate(line) a enumerate(open(fname)) lo arreglaré.

Cuestiones relacionadas