HINWEIS: Dies wurde mit Python 2.7 getestet. Möglicherweise müssen Sie die Dinge in Python 3 zwicken, um Strings vs. Bytes zu behandeln, aber es sollte nicht zu schmerzhaft hoffentlich sein.
Memory-Mapped-Dateien sind möglicherweise nicht ideal für Ihre Situation (32-Bit-Modus erhöht die Wahrscheinlichkeit, dass nicht genügend zusammenhängender virtueller Speicher vorhanden ist, dass nicht aus Pipes oder anderen Nicht-Dateien gelesen werden kann usw.).
Hier ist eine Lösung, die 128k-Blöcke auf einmal liest. Solange Ihre Regex mit einer Zeichenkette übereinstimmt, die kleiner als diese Größe ist, wird dies funktionieren. Beachten Sie auch, dass Sie nicht durch die Verwendung von einzeiligen Regexen eingeschränkt sind. Diese Lösung funktioniert ziemlich schnell, obwohl ich vermute, dass sie geringfügig langsamer ist als die Verwendung von mmap. Es hängt wahrscheinlich mehr davon ab, was Sie mit den Übereinstimmungen machen, sowie die Größe/Komplexität der Regex, nach der Sie suchen.
Die Methode stellt sicher, dass nur maximal 2 Blöcke im Speicher gehalten werden. In einigen Anwendungsfällen kann es sinnvoll sein, mindestens eine Übereinstimmung pro Block zu erzwingen, aber diese Methode kürzt ab, um die maximale Anzahl von 2 Blöcken im Speicher zu halten. Sie stellt auch sicher, dass jede Regex-Übereinstimmung, die bis zum Ende des aktuellen Blocks reicht, NICHT ausgegeben wird und stattdessen die letzte Position für den Fall gespeichert wird, dass entweder die wahre Eingabe erschöpft ist oder wir einen anderen Block haben, auf den die Regex vor dem Ende passt, um Muster wie "[^ \n ]+" oder "xxx$". Sie können zwar immer noch etwas kaputt machen, wenn Sie am Ende der Regex eine Vorausschau wie xx(?!xyz) haben, wobei yz im nächsten Block steht, aber in den meisten Fällen können Sie solche Muster umgehen.
import re
def regex_stream(regex,stream,block_size=128*1024):
stream_read=stream.read
finditer=regex.finditer
block=stream_read(block_size)
if not block:
return
lastpos = 0
for mo in finditer(block):
if mo.end()!=len(block):
yield mo
lastpos = mo.end()
else:
break
while True:
new_buffer = stream_read(block_size)
if not new_buffer:
break
if lastpos:
size_to_append=len(block)-lastpos
if size_to_append > block_size:
block='%s%s'%(block[-block_size:],new_buffer)
else:
block='%s%s'%(block[lastpos:],new_buffer)
else:
size_to_append=len(block)
if size_to_append > block_size:
block='%s%s'%(block[-block_size:],new_buffer)
else:
block='%s%s'%(block,new_buffer)
lastpos = 0
for mo in finditer(block):
if mo.end()!=len(block):
yield mo
lastpos = mo.end()
else:
break
if lastpos:
block=block[lastpos:]
for mo in finditer(block):
yield mo
Zum Testen/Erkunden können Sie dies ausführen:
# NOTE: you can substitute a real file stream here for t_in but using this as a test
t_in=cStringIO.StringIO('testing this is a 1regexxx\nanother 2regexx\nmore 3regexes')
block_size=len('testing this is a regex')
re_pattern=re.compile(r'\dregex+',re.DOTALL)
for match_obj in regex_stream(re_pattern,t_in,block_size=block_size):
print 'found regex in block of len %s/%s: "%s[[[%s]]]%s"'%(
len(match_obj.string),
block_size,match_obj.string[:match_obj.start()].encode('string_escape'),
match_obj.group(),
match_obj.string[match_obj.end():].encode('string_escape'))
Hier ist die Ausgabe:
found regex in block of len 46/23: "testing this is a [[[1regexxx]]]\nanother 2regexx\nmor"
found regex in block of len 46/23: "testing this is a 1regexxx\nanother [[[2regexx]]]\nmor"
found regex in block of len 14/23: "\nmore [[[3regex]]]es"
Dies kann in Verbindung mit Quick-Parsing eine große XML nützlich sein, wo es in Mini-DOMs auf der Grundlage eines Unterelements als Root aufgeteilt werden kann, anstatt in die Handhabung von Callbacks und Zuständen bei der Verwendung eines SAX-Parsers eintauchen zu müssen. Es erlaubt auch, durch XML schneller als gut zu filtern. Aber ich habe es auch für viele andere Zwecke verwendet. Ich bin etwas überrascht, dass solche Rezepte nicht häufiger im Netz zu finden sind!
Noch eine Sache: Das Parsen in Unicode sollte funktionieren, solange der übergebene Stream Unicode-Strings erzeugt, und wenn Sie die Zeichenklassen wie \w müssen Sie das Flag re.U zur re.compile-Musterkonstruktion hinzufügen. In diesem Fall bedeutet block_size tatsächlich die Anzahl der Zeichen und nicht die Anzahl der Bytes.