Rimozione di una sequenza di caratteri da un file binario di grandi dimensioni usando Python

StackOverflow https://stackoverflow.com/questions/221386

  •  03-07-2019
  •  | 
  •  

Domanda

Vorrei tagliare lunghe sequenze dello stesso valore da un file binario in Python. Un modo semplice per farlo è semplicemente leggere il file e usare re.sub per sostituire la sequenza indesiderata. Questo ovviamente non funzionerà su file binari di grandi dimensioni. Può essere fatto in qualcosa come numpy?

È stato utile?

Soluzione

Se non hai la memoria da fare open("big.file").read(), allora numpy non ti aiuterà davvero .. Utilizza la stessa memoria delle variabili python (se hai 1GB di RAM, puoi caricare solo 1GB di dati in numpy )

La soluzione è semplice: leggi il file in blocchi. f = open("big.file", "rb"), quindi esegui una serie di f.read(500), rimuovi la sequenza e riscrivila su un altro oggetto file. Praticamente come si fa a leggere / scrivere file in C ..

Il problema quindi è se ti manca lo schema che stai sostituendo. Ad esempio:

target_seq = "567"
input_file = "1234567890"

target_seq.read(5) # reads 12345, doesn't contain 567
target_seq.read(5) # reads 67890, doesn't contain 567

La soluzione ovvia è iniziare dal primo carattere nel file, selezionare len(target_seq) caratteri, quindi andare avanti di un carattere, controllare di nuovo in avanti.

Ad esempio (pseudo codice!):

while cur_data != "":
    seek_start = 0
    chunk_size = len(target_seq)

    input_file.seek(offset = seek_start, whence = 1) #whence=1 means seek from start of file (0 + offset)
    cur_data = input_file.read(chunk_size) # reads 123
    if target_seq == cur_data:
        # Found it!
        out_file.write("replacement_string")
    else:
        # not it, shove it in the new file
        out_file.write(cur_data)
    seek_start += 1

Non è esattamente il modo più efficiente, ma funzionerà e non richiederà di conservare una copia del file (o due).

Altri suggerimenti

Se due copie si adattano alla memoria, allora puoi facilmente fare una copia. La seconda copia è la versione compressa. Certo, puoi usare numpy, ma puoi anche usare l'array pacchetto. Inoltre, puoi trattare il tuo grande oggetto binario come una stringa di byte e manipolarlo direttamente.

Sembra che il tuo file possa essere DAVVERO di grandi dimensioni e non puoi inserire due copie in memoria. (Non hai fornito molti dettagli, quindi questa è solo una supposizione.) Dovrai fare la compressione in blocchi. Leggerai in un pezzo, eseguirai qualche elaborazione su quel pezzo e lo scriverai. Anche in questo caso, numpy, array o semplice stringa di byte funzionerà correttamente.

La soluzione di dbr è una buona idea, ma un po 'troppo complicata tutto ciò che devi fare è riavvolgere il puntatore del file per la lunghezza della sequenza che stai cercando, prima di leggere il tuo prossimo pezzo.

def ReplaceSequence(inFilename, outFilename, oldSeq, newSeq):
 inputFile  = open(inFilename, "rb")
 outputFile = open(outFilename, "wb")

 data = ""
 chunk = 1024

 while 1:
      data = inputFile.read(chunk)
      data = data.replace(oldSeq, newSeq)
      outputFile.write(data)

      inputFile.seek(-len(oldSequence), 1)
      outputFile.seek(-len(oldSequence), 1)

     if len(data) < chunk:
           break

 inputFile.close()
 outputFile.close()

Il suggerimento AJMayorga va bene a meno che le dimensioni delle stringhe di ricambio siano diverse. Oppure la stringa di sostituzione si trova alla fine del blocco.

L'ho corretto in questo modo:

def ReplaceSequence(inFilename, outFilename, oldSeq, newSeq):
    inputFile  = open(inFilename, "rb")
    outputFile = open(outFilename, "wb")

data = ""
chunk = 1024

oldSeqLen = len(oldSeq)

while 1:
    data = inputFile.read(chunk)

    dataSize = len(data)
    seekLen= dataSize - data.rfind(oldSeq) - oldSeqLen
    if seekLen > oldSeqLen:
        seekLen = oldSeqLen

    data = data.replace(oldSeq, newSeq)
    outputFile.write(data)
    inputFile.seek(-seekLen, 1) 
    outputFile.seek(-seekLen, 1)

    if dataSize < chunk:
        break

inputFile.close()
outputFile.close()

Devi rendere la tua domanda più precisa. Conosci i valori che vuoi tagliare in anticipo?

Supponendo che lo faccia, probabilmente cercherei le sezioni corrispondenti usando sottoprocesso per eseguire " fgrep -o -b < cerca stringa > " e quindi modificare le sezioni pertinenti del file utilizzando i metodi file dell'oggetto cerca , read e write dell'oggetto <. / p>

Questa versione basata su generatore manterrà esattamente un carattere del contenuto del file in memoria alla volta.

Nota che prendo il titolo della tua domanda piuttosto letteralmente: vuoi ridurre le esecuzioni dello stesso carattere a un singolo personaggio. Per sostituire i pattern in generale, questo non funziona:

import StringIO

def gen_chars(stream):
   while True:
      ch = stream.read(1)
      if ch: 
         yield ch
      else:
         break

def gen_unique_chars(stream):
   lastchar = ''
   for char in gen_chars(stream):
      if char != lastchar:
         yield char
      lastchar=char

def remove_seq(infile, outfile):
   for ch in gen_unique_chars(infile):
      outfile.write(ch)

# Represents a file open for reading
infile  = StringIO.StringIO("1122233333444555")

# Represents a file open for writing
outfile = StringIO.StringIO()

# Will print "12345"
remove_seq(infile, outfile)
outfile.seek(0)
print outfile.read()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top