Entfernen eine Folge von Zeichen aus einer großen Binär-Datei mit Python
-
03-07-2019 - |
Frage
Ich mag lange Sequenzen mit dem gleichen Wert aus einer Binärdatei in Python trimmen. Eine einfache Möglichkeit, es zu tun liest einfach in der Datei und mit re.sub der unerwünschten Folge zu ersetzen. Dies wird natürlich nicht auf große binäre Dateien. Kann es in so etwas wie numpy getan werden?
Lösung
Wenn Sie nicht über den Speicher open("big.file").read()
zu tun, dann wirklich numpy nicht helfen .. Es verwendet den gleichen Speicher wie Python Variablen haben (wenn Sie 1 GB RAM haben, können Sie nur 1 GB Daten in numpy laden)
Die Lösung ist einfach - die Datei in Stücke lesen .. f = open("big.file", "rb")
, dann eine Reihe von f.read(500)
, die Sequenz entfernen und schreiben Sie es heraus in ein anderes Dateiobjekt zurück. Ziemlich viel, wie Sie tun, Datei lesen / in C zu schreiben ..
Das Problem ist dann, wenn Sie das Muster, das Sie ersetzen verpassen .. Zum Beispiel:
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
Die offensichtliche Lösung ist mit dem ersten Zeichen in der Datei, überprüft len(target_seq)
Zeichen, dann gehen Sie ein Zeichen vorwärts, überprüft wieder nach vorne beginnen.
Zum Beispiel (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
Es ist nicht gerade die effizienteste Art und Weise, aber es wird funktionieren, und erfordert nicht eine Kopie der Datei im Speicher (oder zwei) zu halten.
Andere Tipps
Wenn zwei Kopien in den Speicher passen, dann können Sie ganz einfach eine Kopie. Die zweite Kopie ist die komprimierte Version. Sicher, können Sie numpy, aber Sie können auch die Array Paket. Darüber hinaus können Sie Ihr großes binäres Objekt als eine Kette von Bytes behandeln und es direkt manipulieren.
Es klingt wie Ihre Datei sein kann wirklich groß, und man kann nicht zwei Kopien in den Speicher passen. (Sie haben nicht viele Details liefern, so ist dies nur eine Vermutung.) Sie werden Ihre Kompression in Stücke zu tun haben. Sie werden in einem Stück lesen, auf diesem Brocken einige Verarbeitung tun und schreiben Sie es heraus. Auch hier wird numpy, Array oder einfache Kette von Bytes funktionieren.
dbr-Lösung ist eine gute Idee, aber ein bisschen zu kompliziert alles, was Sie wirklich tun müssen, ist die Datei zurückspulen die Länge des Sequenzzeiger Sie suchen, bevor Sie Ihren nächsten Brocken lesen.
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()
AJMayorga Vorschlag ist in Ordnung, es sei denn, die Größen der Ersatzzeichenfolgen unterschiedlich sind. Oder die Ersatzzeichenfolge ist am Ende des Blocks.
ich es wie folgt festgelegt:
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()
Sie benötigen eine Frage präziser zu machen. Haben Sie die Werte wissen Sie vor der Zeit trimmen wollen?
Angenommen, Sie tun, würde ich wahrscheinlich für die passenden Abschnitte mit subprocess
suchen „fgrep -o -b <search string>
“ laufen und dann den entsprechenden Abschnitten der Datei ändert den Python file
Objekts seek
, read
und write
Methoden verwenden.
Dieser Generator-basierte Version hält genau ein Zeichen des Dateiinhalts im Speicher auf einmal.
Beachten Sie, dass ich Ihre Frage Titel ganz wörtlich nehme - wollen Sie läuft mit dem gleichen Charakter auf ein einzelnes Zeichen zu reduzieren. Für Muster im Allgemeinen ersetzen, funktioniert das nicht:
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()