2013-04-29 4 views
11

Ho un file di testo dire really_big_file.txt che contiene:Dividere file di testo di grandi dimensioni in piccoli file di testo per i numeri di riga utilizzando Python

line 1 
line 2 
line 3 
line 4 
... 
line 99999 
line 100000 

vorrei scrivere uno script Python che divide in piccoli really_big_file.txt file con 300 righe ciascuno. Ad esempio, small_file_300.txt per avere le linee 1-300, small_file_600 per avere le linee 301-600 e così via fino a quando non ci sono abbastanza piccoli file fatti per contenere tutte le linee dal grande file.

Gradirei qualche suggerimento sul modo più semplice per ottenere questo risultato utilizzando Python

risposta

17

Utilizzando itertools grouper ricetta:

from itertools import izip_longest 

def grouper(n, iterable, fillvalue=None): 
    "Collect data into fixed-length chunks or blocks" 
    # grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx 
    args = [iter(iterable)] * n 
    return izip_longest(fillvalue=fillvalue, *args) 

n = 300 

with open('really_big_file.txt') as f: 
    for i, g in enumerate(grouper(n, f, fillvalue=''), 1): 
     with open('small_file_{0}'.format(i * n), 'w') as fout: 
      fout.writelines(g) 

Il vantaggio di questo metodo in contrasto con la memorizzazione di ogni riga in un elenco, è che funziona con iterables, riga per riga, quindi non deve memorizzare ogni small_file in memoria in una sola volta.

Si noti che l'ultimo file in questo caso sarà small_file_100200 ma andrà solo fino a line 100000. Questo accade perché fillvalue='', ovvero non scrivo nel file quando non ho più righe da scrivere perché una dimensione di gruppo non divide equamente. Puoi risolvere questo problema scrivendo su un file temporaneo e poi rinominandolo dopo, invece di nominarlo come prima. Ecco come si può fare.

import os, tempfile 

with open('really_big_file.txt') as f: 
    for i, g in enumerate(grouper(n, f, fillvalue=None)): 
     with tempfile.NamedTemporaryFile('w', delete=False) as fout: 
      for j, line in enumerate(g, 1): # count number of lines in group 
       if line is None: 
        j -= 1 # don't count this line 
        break 
       fout.write(line) 
     os.rename(fout.name, 'small_file_{0}.txt'.format(i * n + j)) 

Questa volta il fillvalue=None e passare attraverso ogni linea di controllo per None, quando si verifica, so che il processo è finito così mi sottraggo 1 da j di non contare il riempimento e poi scrivere il file.

+1

Se si utilizza il primo script in python 3.x, sostituire '' 'izip_longest''' con il nuovo' '' zip_longest''' https://docs.python.org/3/library/ itertools.html # itertools.zip_longest –

0
lines_per_file = 300 # Lines on each small file 
lines = [] # Stores lines not yet written on a small file 
lines_counter = 0 # Same as len(lines) 
created_files = 0 # Counting how many small files have been created 

with open('really_big_file.txt') as big_file: 
    for line in big_file: # Go throught the whole big file 
     lines.append(line) 
     lines_counter += 1 
     if lines_counter == lines_per_file: 
      idx = lines_per_file * (created_files + 1) 
      with open('small_file_%s.txt' % idx, 'w') as small_file: 
       # Write all lines on small file 
       small_file.write('\n'.join(stored_lines)) 
      lines = [] # Reset variables 
      lines_counter = 0 
      created_files += 1 # One more small file has been created 
    # After for-loop has finished 
    if lines_counter: # There are still some lines not written on a file? 
     idx = lines_per_file * (created_files + 1) 
     with open('small_file_%s.txt' % idx, 'w') as small_file: 
      # Write them on a last small file 
      small_file.write('n'.join(stored_lines)) 
     created_files += 1 

print '%s small files (with %s lines each) were created.' % (created_files, 
                  lines_per_file) 
+0

L'unica cosa è che devi memorizzare ogni 'small_file' nella memoria in una volta prima di scriverlo con questo metodo, che può o m non è un problema però Ovviamente è possibile risolvere questo problema semplicemente scrivendo per scrivere sul file riga per riga. – jamylak

2

faccio questo un modo più comprensibile e con tagli meno brevi, al fine di dare una maggiore comprensione di come e perché funziona. Le risposte precedenti funzionano, ma se non hai familiarità con certe funzioni integrate, non capirai cosa sta facendo la funzione.

Poiché non hai pubblicato alcun codice, ho deciso di farlo in questo modo poiché potresti non conoscere cose diverse dalla sintassi python di base dato che il modo in cui hai formulato la domanda ha fatto sembrare che non avessi provato né avuto alcun indizio come come affrontare la questione

Ecco i passaggi per fare questo in Python base:

in primo luogo si dovrebbe leggere il file in un elenco per la custodia:

my_file = 'really_big_file.txt' 
hold_lines = [] 
with open(my_file,'r') as text_file: 
    for row in text_file: 
     hold_lines.append(row) 

secondo luogo, è necessario impostare un modo di creare il nuovo file per nome!Vorrei suggerire un ciclo con un paio di contatori:

outer_count = 1 
line_count = 0 
sorting = True 
while sorting: 
    count = 0 
    increment = (outer_count-1) * 300 
    left = len(hold_lines) - increment 
    file_name = "small_file_" + str(outer_count * 300) + ".txt" 

In terzo luogo, all'interno di quel ciclo avete bisogno di alcuni cicli annidati che salveranno le righe corrette in un array:

hold_new_lines = [] 
    if left < 300: 
     while count < left: 
      hold_new_lines.append(hold_lines[line_count]) 
      count += 1 
      line_count += 1 
     sorting = False 
    else: 
     while count < 300: 
      hold_new_lines.append(hold_lines[line_count]) 
      count += 1 
      line_count += 1 

Ultima cosa, di nuovo in il vostro primo ciclo è necessario scrivere il nuovo file e aggiungere il vostro ultimo incremento del contatore in modo che il ciclo passerà attraverso ancora e scrivere un nuovo file

outer_count += 1 
with open(file_name,'w') as next_file: 
    for row in hold_new_lines: 
     next_file.write(row) 

nota: se il numero di righe non è divisibile b y 300, l'ultimo file avrà un nome che non corrisponde all'ultima riga del file.

E 'importante capire perché questi cicli di lavoro. È stato impostato in modo che nel ciclo successivo, il nome del file che si scrive cambi perché il nome dipende da una variabile variabile. Si tratta di uno strumento di scripting molto utile per l'accesso al file, l'apertura, la scrittura, l'organizzazione ecc

Nel caso in cui non si poteva seguire quello che era in quello ciclo, qui è la totalità della funzione:

my_file = 'really_big_file.txt' 
sorting = True 
hold_lines = [] 
with open(my_file,'r') as text_file: 
    for row in text_file: 
     hold_lines.append(row) 
outer_count = 1 
line_count = 0 
while sorting: 
    count = 0 
    increment = (outer_count-1) * 300 
    left = len(hold_lines) - increment 
    file_name = "small_file_" + str(outer_count * 300) + ".txt" 
    hold_new_lines = [] 
    if left < 300: 
     while count < left: 
      hold_new_lines.append(hold_lines[line_count]) 
      count += 1 
      line_count += 1 
     sorting = False 
    else: 
     while count < 300: 
      hold_new_lines.append(hold_lines[line_count]) 
      count += 1 
      line_count += 1 
    outer_count += 1 
    with open(file_name,'w') as next_file: 
     for row in hold_new_lines: 
      next_file.write(row) 
+0

Eccellente @Ryan Saxe! – Lucas

11
lines_per_file = 300 
smallfile = None 
with open('really_big_file.txt') as bigfile: 
    for lineno, line in enumerate(bigfile): 
     if lineno % lines_per_file == 0: 
      if smallfile: 
       smallfile.close() 
      small_filename = 'small_file_{}.txt'.format(lineno + lines_per_file) 
      smallfile = open(small_filename, "w") 
     smallfile.write(line) 
    if smallfile: 
     smallfile.close() 
+0

Nizza, a breve codice e funziona come fascino – MoizNgp

3
import csv 
import os 
import re 

MAX_CHUNKS = 300 


def writeRow(idr, row): 
    with open("file_%d.csv" % idr, 'ab') as file: 
     writer = csv.writer(file, delimiter=',', quotechar='\"', quoting=csv.QUOTE_ALL) 
     writer.writerow(row) 

def cleanup(): 
    for f in os.listdir("."): 
     if re.search("file_.*", f): 
      os.remove(os.path.join(".", f)) 

def main(): 
    cleanup() 
    with open("large_file.csv", 'rb') as results: 
     r = csv.reader(results, delimiter=',', quotechar='\"') 
     idr = 1 
     for i, x in enumerate(r): 
      temp = i + 1 
      if not (temp % (MAX_CHUNKS + 1)): 
       idr += 1 
      writeRow(idr, x) 

if __name__ == "__main__": main() 
+0

Hey domanda veloce, ti dispiacerebbe che spiega il motivo per cui utilizzando quotechar = '\ "' grazie – Jiraheta

+0

stavo usando come ho avuto un char citazione diversa (|). Nel mio caso, è possibile saltare l'impostazione di questa uno come il carattere di citazione di default è (virgolette ") – Varun

+0

Per le persone che sono preoccupati per la velocità, un file CSV con 98500 record (e circa 13MB di dimensione) è stato diviso con questo codice in circa 2,31 secondi. Direi che è abbastanza buono. –