2010-02-01 13 views
6

Ho una cartella con file di testo 100k. Voglio mettere file con oltre 20 linee in un'altra cartella. Come lo faccio in Python? Ho usato os.listdir, ma ovviamente non c'è abbastanza memoria per caricare anche i nomi dei file in memoria. C'è un modo per ottenere forse 100 nomi di file alla volta?Filtra i file in una cartella molto grande

Ecco il mio codice:

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 

e di uscita:

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

Ecco lo script modificato:

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 

funziona su una cartella con un minor numero di file, ma con la più grande cartella, tutto ciò che stampa è "0" ... Funziona su server linux, stampa 0 su mac ... oh beh ...

+3

"non c'è abbastanza memoria per caricare anche i nomi dei file in memoria" Davvero? I nomi di file 100K non sono poi così tanta memoria. Che errore stai ottenendo? Puoi pubblicare lo snippet di codice? –

+1

Perché la memoria è un problema? File 100k con nomi di, ad esempio, 10 caratteri ciascuno, è 10^7 byte = 10 megabyte, non troppo grandi in realtà. –

+0

Sono d'accordo che una OOM è strana. Cosa succede se inserisci 'filenames = os.listdir ('/ somedir/labels /')' al REPL? –

risposta

4

si potrebbe provare a utilizzare glob.iglob che restituisce un iteratore:

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

Inoltre, si prega di non utilizzare dir per un nome di variabile: si sta shadowing il built-in.

Un altro miglioramento importante che è possibile introdurre è la funzione filelen. Se lo sostituisci con il seguente, risparmierai un sacco di tempo. Fidati di me, 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

Qualcuno ha letto la funzione 'many_line' prima di premere il pulsante upvote ??? –

+0

@ John: qualcuno qui può distinguere l'errore di battitura dal vero problema? – SilentGhost

+0

+1 il premio di battesimo più grasso dell'anno –

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

Questa è l'attività di base che cstico sta cercando di realizzare, ma non è una risposta alla sua domanda. – jcdyer

+0

sì, lo è. Ha chiesto come mettere file con oltre 20 linee in un'altra cartella usando Python. – ghostdog74

+2

No, ha chiesto come farlo per una directory con 100.000 file, notando che chiamare os.listdir ("."), Come si fa, significa che esaurisce la memoria. –

2

Un paio di pensieri. Innanzitutto, è possibile utilizzare il modulo glob per ottenere gruppi di file più piccoli. In secondo luogo, l'ordinamento in base al numero di righe richiederà molto tempo, poiché è necessario aprire ogni file e contare le linee. Se è possibile partizionare per conteggio dei byte, è possibile evitare di aprire i file utilizzando il modulo stat. Se è cruciale che la divisione avvenga su 20 righe, puoi almeno ritagliare grandi file di file individuando un numero minimo di caratteri che un file di 20 righe del tuo tipo avrà e non aprendo file più piccoli di quello.

0

come utilizzare uno script di shell? si poteva scegliere un file alla volta:

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

ppl si prega di correggere se sbaglio in alcun modo

+1

non utilizzare ls con ciclo for like. Si rompe su file con spazi. Utilizza l'espansione della shell. – ghostdog74

0

La risposta attualmente accettato semplicemente non funziona. Questa funzione:

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

ha due problemi: in primo luogo, il fname arg non viene utilizzato e il file non viene aperto. In secondo luogo, la chiamata a enumerate(line) non riuscirà perché line non è definito.

Modifica enumerate(line) a enumerate(open(fname)) lo risolverà.

Problemi correlati