Come faccio a dividere un grande file di testo in python

Ho un enorme file di testo (~1GB) e purtroppo l’editor di testo utilizzare non leggere un file di grandi dimensioni. Tuttavia, se posso dividere in due o tre parti sarò bene, quindi, come un esercizio che ho voluto scrivere un programma in python per farlo.

Di quello che penso, voglio che il programma di fare è trovare la dimensione di un file, dividere in parti, e per ogni parte, leggere fino a che punto in blocchi, la scrittura di un filename.nnn file di output, quindi leggere fino alla successiva interruzione di riga e di scrittura, quindi chiudere il file di output, etc. Ovviamente l’ultimo file di output solo le copie alla fine del file di input.

Mi potete aiutare con la chiave filesystem parti correlate: dimensione file, leggere e scrivere in blocchi e la lettura di una interruzione di linea?

Io a scrivere questo codice prima i test, quindi non c’è bisogno di darmi una risposta completa, a meno che la sua una battuta 😉

  • Sgradite suggerimento: è meglio avere un editor di testo. 🙂 Se sei su Windows, EmEditor è un so di che consente la modifica dei file senza dover caricare tutto in memoria.
InformationsquelleAutor quamrana | 2008-11-14



14 Replies
  1. 15

    Check out os.stat() per la dimensione del file e file.readlines([sizehint]). Queste due funzioni non dovrebbe essere tutto il necessario per la lettura di parte, e, auspicabilmente, si sa come fare la scritta 🙂

    • Grazie per la risposta, i tuoi suggerimenti sono funzionando bene finora per la lettura del file. Quando ho finito, provo anche una versione binaria che non legge una riga alla volta.
    • Cosa c’è di sbagliato con os.path.getsize(filename)?
  2. 32

    linux ha un comando split

    split-l 100000 file.txt

    sarebbe suddiviso in file di dimensioni pari a 100.000 dimensione della linea

    • E se tu sei di base del sistema operativo Windows è possibile ottenere Cygwin per accedere praticamente a tutte le cool utilità della riga di comando.
    • Unixtools per windows hanno anche lo strumento di divisione: split.exe
    • Ho un 120 GB di file. Durante l’utilizzo di questo comando è sempre bloccato dopo alcuni 1928613 linee. Non proceda ulteriori. Stavo cercando di fare ciò che è stato detto in stackoverflow.com/a/291759/6143004 ma lo stesso problema si verifichi.
  3. 9

    Come un metodo alternativo, utilizzando la libreria di registrazione:

    >>> import logging.handlers
    >>> log = logging.getLogger()
    >>> fh = logging.handlers.RotatingFileHandler("D://filename.txt", 
         maxBytes=2**20*100, backupCount=100) 
    # 100 MB each, up to a maximum of 100 files
    >>> log.addHandler(fh)
    >>> log.setLevel(logging.INFO)
    >>> f = open("D://biglog.txt")
    >>> while True:
    ...     log.info(f.readline().strip())

    File verrà visualizzato come segue:

    filename.txt (end of file)

    il nome del file.txt.1

    il nome del file.txt.2



    il nome del file.txt.10 (inizio del file)

    Questo è un modo facile e veloce per fare un enorme file di registro di abbinare il vostro RotatingFileHandler attuazione.

    • dal momento che si divide riga per riga, come fare più in fretta?
  4. 5

    Questo generatore è un metodo (lento) per ottenere una fetta di linee senza saltare in aria la vostra memoria.

    import itertools
    
    def slicefile(filename, start, end):
        lines = open(filename)
        return itertools.islice(lines, start, end)
    
    out = open("/blah.txt", "w")
    for line in slicefile("/python27/readme.txt", 10, 15):
        out.write(line)
  5. 4

    È possibile utilizzare wc e split (consultare le rispettive pagine di manuale) per ottenere l’effetto desiderato. In bash:

    split -dl$((`wc -l 'filename'|sed 's/.*$//'` / 3 + 1)) filename filename-chunk.

    produce 3 parti di uno stesso linecount (con un errore di arrotondamento in ultimo, ovviamente), denominato filename-chunk.00 per filename-chunk.02.

    • Sì, non è Python, ma perché utilizzare un cacciavite per applicare un chiodo?
    • Beh, non è davvero un cacciavite vs chiodo… python spesso è un ottimo modo per svolgere compiti semplici come questo. E non voglio bash bash (pun intended), ma che non è molto leggibile…:)
    • È molto leggibile, è solo bisogno di conoscere la lingua.
    • Das kan jeder sagen
    • Bene, rückblickend würde ich vielleicht ea awk '{print $1}' statt der sed-Konstruktion verwenden. Trotzdem kann man ziemlich direkt sehen, era passiert: wc zählt morire Zeilen, sed zieht die reine Zahl aus der Ausgabe, die wird durch drei geteilt und um 1 erhöht; split crea file pdf partendo dann Teile dieser Länge aus filename und benennt sie filename.chunk. plus fortlaufende Nummer. Es wäre natürlich nett, wenn wc eine Opzione hätte, direkt nur die Zahl auszugeben, aber auch così kann man damit intestino arbeiten.
    • Qualcuno in questo thread appena ricevuto epicamente detto.

  6. 4

    non dimenticare seek() e mmap() per l’accesso casuale ai file.

    def getSomeChunk(filename, start, len):
        fobj = open(filename, 'r+b')
        m = mmap.mmap(fobj.fileno(), 0)
        return m[start:start+len]
  7. 4

    Mentre Ryan Ginstrom risposta è corretto, ci vuole più tempo di quanto dovrebbe (come ha già notato). Ecco un modo per aggirare il più chiamate a itertools.islice successivamente scorrere il descrittore del file aperto:

    def splitfile(infilepath, chunksize):
        fname, ext = infilepath.rsplit('.',1)
        i = 0
        written = False
        with open(infilepath) as infile:
            while True:
                outfilepath = "{}{}.{}".format(fname, i, ext)
                with open(outfilepath, 'w') as outfile:
                    for line in (infile.readline() for _ in range(chunksize)):
                        outfile.write(line)
                    written = bool(line)
                if not written:
                    break
                i += 1
  8. 2

    Ho scritto il programma e sembra funzionare bene. E così, grazie a Kamil Kisiel per avermi iniziato.

    (Nota che FileSizeParts() è una funzione non mostrato qui)

    Poi potrei ovviare a fare una versione che fa un binario di lettura per vedere se più veloce.

    def Split(inputFile,numParts,outputName):
        fileSize=os.stat(inputFile).st_size
        parts=FileSizeParts(fileSize,numParts)
        openInputFile = open(inputFile, 'r')
        outPart=1
        for part in parts:
            if openInputFile.tell()<fileSize:
                fullOutputName=outputName+os.extsep+str(outPart)
                outPart+=1
                openOutputFile=open(fullOutputName,'w')
                openOutputFile.writelines(openInputFile.readlines(part))
                openOutputFile.close()
        openInputFile.close()
        return outPart-1
  9. 2

    di utilizzo – split.py filename splitsizeinkb

    import os
    import sys
    
    def getfilesize(filename):
       with open(filename,"rb") as fr:
           fr.seek(0,2) # move to end of the file
           size=fr.tell()
           print("getfilesize: size: %s" % size)
           return fr.tell()
    
    def splitfile(filename, splitsize):
       # Open original file in read only mode
       if not os.path.isfile(filename):
           print("No such file as: \"%s\"" % filename)
           return
    
       filesize=getfilesize(filename)
       with open(filename,"rb") as fr:
        counter=1
        orginalfilename = filename.split(".")
        readlimit = 5000 #read 5kb at a time
        n_splits = filesize//splitsize
        print("splitfile: No of splits required: %s" % str(n_splits))
        for i in range(n_splits+1):
            chunks_count = int(splitsize)//int(readlimit)
            data_5kb = fr.read(readlimit) # read
            # Create split files
            print("chunks_count: %d" % chunks_count)
            with open(orginalfilename[0]+"_{id}.".format(id=str(counter))+orginalfilename[1],"ab") as fw:
                fw.seek(0) 
                fw.truncate()# truncate original if present
                while data_5kb:                
                    fw.write(data_5kb)
                    if chunks_count:
                        chunks_count-=1
                        data_5kb = fr.read(readlimit)
                    else: break            
            counter+=1 
    
    if __name__ == "__main__":
       if len(sys.argv) < 3: print("Filename or splitsize not provided: Usage:     filesplit.py filename splitsizeinkb ")
       else:
           filesize = int(sys.argv[2]) * 1000 #make into kb
           filename = sys.argv[1]
           splitfile(filename, filesize)
    • Per me ha funzionato perfettamente nel 2017! Grazie mille @Mudit
    • Si può fare questo codice estratto di linea per linea, non charcter da personaggio a personaggio. C’è un modo si può ottenere il numero di caratteri nella riga successiva?
  10. 1

    Questo ha funzionato per me

    import os
    
    fil = "inputfile"
    outfil = "outputfile"
    
    f = open(fil,'r')
    
    numbits = 1000000000
    
    for i in range(0,os.stat(fil).st_size/numbits+1):
        o = open(outfil+str(i),'w')
        segment = f.readlines(numbits)
        for c in range(0,len(segment)):
            o.write(segment[c]+"\n")
        o.close()
  11. 0

    O, una versione di python di wc e split:

    lines = 0
    for l in open(filename): lines += 1

    Quindi un po ‘ di codice per leggere le prime righe/3 in un unico file, il prossimo linee/3 in un altro , etc.

    • Non c’è bisogno di tenere il conteggio manualmente, utilizzare enumerare: l, linea in enumerate(open(filename)):…
  12. 0

    Ho avuto necessità di dividere un file csv per l’importazione in Dynamics CRM in quanto il limite di dimensione del file per l’importazione è di 8 mb e il file che riceviamo sono molto più grandi. Questo programma permette di inserire i Nomi di file e LinesPerFile, e poi si divide i file specificati nella richiesta il numero di linee. Non ci posso credere quanto velocemente funziona!

    # user input FileNames and LinesPerFile
    FileCount = 1
    FileNames = []
    while True:
        FileName = raw_input('File Name ' + str(FileCount) + ' (enter "Done" after last File):')
        FileCount = FileCount + 1
        if FileName == 'Done':
            break
        else:
            FileNames.append(FileName)
    LinesPerFile = raw_input('Lines Per File:')
    LinesPerFile = int(LinesPerFile)
    
    for FileName in FileNames:
        File = open(FileName)
    
        # get Header row
        for Line in File:
            Header = Line
            break
    
        FileCount = 0
        Linecount = 1
        for Line in File:
    
            #skip Header in File
            if Line == Header:
                continue
    
            #create NewFile with Header every [LinesPerFile] Lines
            if Linecount % LinesPerFile == 1:
                FileCount = FileCount + 1
                NewFileName = FileName[:FileName.find('.')] + '-Part' + str(FileCount) + FileName[FileName.find('.'):]
                NewFile = open(NewFileName,'w')
                NewFile.write(Header)
    
            NewFile.write(Line)
            Linecount = Linecount + 1
    
        NewFile.close()
  13. 0

    Qui è uno script python è possibile utilizzare per dividere file di grandi dimensioni utilizzando subprocess:

    """
    Splits the file into the same directory and
    deletes the original file
    """
    
    import subprocess
    import sys
    import os
    
    SPLIT_FILE_CHUNK_SIZE = '5000'
    SPLIT_PREFIX_LENGTH = '2'  # subprocess expects a string, i.e. 2 = aa, ab, ac etc..
    
    if __name__ == "__main__":
    
        file_path = sys.argv[1]
        # i.e. split -a 2 -l 5000 t/some_file.txt ~/tmp/t/
        subprocess.call(["split", "-a", SPLIT_PREFIX_LENGTH, "-l", SPLIT_FILE_CHUNK_SIZE, file_path,
                         os.path.dirname(file_path) + '/'])
    
        # Remove the original file once done splitting
        try:
            os.remove(file_path)
        except OSError:
            pass

    Si può chiamare esternamente:

    import os
    fs_result = os.system("python file_splitter.py {}".format(local_file_path))

    È anche possibile importare subprocess ed eseguire direttamente nel programma.

    Il problema con questo approccio è elevato utilizzo della memoria: subprocess crea una forcella con un footprint di memoria della stessa dimensione come il vostro processo e se il processo di memoria è già pesante, si raddoppia per il tempo che corre. La stessa cosa con os.system.

    Qui è un altro puro python modo di fare questo, anche se non ho testato su file di grandi dimensioni, sarà più lento ma più magro di memoria:

    CHUNK_SIZE = 5000
    
    def yield_csv_rows(reader, chunk_size):
        """
        Opens file to ingest, reads each line to return list of rows
        Expects the header is already removed
        Replacement for ingest_csv
        :param reader: dictReader
        :param chunk_size: int, chunk size
        """
        chunk = []
        for i, row in enumerate(reader):
            if i % chunk_size == 0 and i > 0:
                yield chunk
                del chunk[:]
            chunk.append(row)
        yield chunk
    
    with open(local_file_path, 'rb') as f:
        f.readline().strip().replace('"', '')
        reader = unicodecsv.DictReader(f, fieldnames=header.split(','), delimiter=',', quotechar='"')
        chunks = yield_csv_rows(reader, CHUNK_SIZE)
        for chunk in chunks:
            if not chunk:
                break
            # Do something with your chunk here

    Qui è un altro esempio di utilizzo readlines():

    """
    Simple example using readlines()
    where the 'file' is generated via:
    seq 10000 > file
    """
    CHUNK_SIZE = 5
    
    
    def yield_rows(reader, chunk_size):
        """
        Yield row chunks
        """
        chunk = []
        for i, row in enumerate(reader):
            if i % chunk_size == 0 and i > 0:
                yield chunk
                del chunk[:]
            chunk.append(row)
        yield chunk
    
    
    def batch_operation(data):
        for item in data:
            print(item)
    
    
    with open('file', 'r') as f:
        chunks = yield_rows(f.readlines(), CHUNK_SIZE)
        for _chunk in chunks:
            batch_operation(_chunk)

Lascia un commento