Suppression d'une séquence de caractères d'un fichier binaire volumineux à l'aide de python
-
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?
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.
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 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()