Suppression d'une séquence de caractères d'un fichier binaire volumineux à l'aide de python

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

  •  03-07-2019
  •  | 
  •  

Question

Je voudrais couper de longues séquences de la même valeur à partir d'un fichier binaire en python. Une façon simple de le faire est simplement de lire le fichier et d’utiliser re.sub pour remplacer la séquence indésirable. Cela ne fonctionnera évidemment pas sur les gros fichiers binaires. Peut-on le faire avec quelque chose comme Numpy?

Était-ce utile?

La solution

Si vous n'avez pas assez de mémoire pour faire open("big.file").read(), numpy ne vous aidera pas vraiment. Il utilise la même mémoire que les variables python (si vous avez 1 Go de RAM, vous ne pouvez charger que 1 Go de données dans numpy. )

La solution est simple: lisez le fichier par fragments .. f = open("big.file", "rb"), puis effectuez une série de f.read(500), supprimez la séquence et réécrivez-la dans un autre objet fichier. En gros comment vous faites la lecture / écriture de fichiers en C ..

Le problème est alors si vous manquez le modèle que vous remplacez. Par exemple:

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 solution évidente consiste à commencer par le premier caractère du fichier, à cocher len(target_seq) caractères, puis à avancer d'un caractère, à vérifier à nouveau.

Par exemple (pseudo-code!):

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

Ce n'est pas exactement le moyen le plus efficace, mais cela fonctionnera et ne nécessitera pas de conserver une copie du fichier en mémoire (ou deux).

Autres conseils

Si deux copies tiennent dans la mémoire, vous pouvez facilement en faire une copie. La deuxième copie est la version compressée. Bien sûr, vous pouvez utiliser numpy, mais vous pouvez également utiliser le tableau package. De plus, vous pouvez traiter votre gros objet binaire comme une chaîne d'octets et le manipuler directement.

Il semble que votre fichier soit VRAIMENT et que vous ne puissiez pas stocker deux copies en mémoire. (Vous n'avez pas fourni beaucoup de détails, alors ce n'est qu'une supposition.) Vous devrez faire votre compression en morceaux. Vous allez lire dans un morceau, faire un traitement sur ce morceau et l'écrire. Encore une fois, numpy, array ou une simple chaîne d'octets fonctionneront correctement.

La solution de

dbr est une bonne idée, mais un peu trop compliquée, il vous suffit de rembobiner le pointeur de fichier sur la longueur de la séquence recherchée, avant de lire votre prochain bloc.

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()

La suggestion d'AJMayorga est acceptable sauf si les tailles des chaînes de remplacement sont différentes. Ou la chaîne de remplacement est à la fin du morceau.

Je l'ai corrigé comme ceci:

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()

Vous devez préciser votre question. Connaissez-vous les valeurs que vous souhaitez supprimer à l’avance?

En supposant que ce soit le cas, je rechercherais probablement les sections correspondantes en utilisant le sous-processus pour exécuter le script " fgrep -o -b-l"; chaîne de recherche > " puis modifiez les sections appropriées du fichier à l'aide des méthodes seek , read et write de l'objet python.

Cette version basée sur un générateur garde exactement un caractère du contenu du fichier en mémoire à la fois.

Notez que je prends le titre de votre question assez littéralement. Vous souhaitez réduire les exécutions du même caractère à un seul caractère. Pour remplacer les modèles en général, cela ne fonctionne pas:

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()
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top