NOTA: esto ha sido probado en python2.7. Puede que tenga que ajustar cosas en python 3 para manejar cadenas en vez de bytes, pero no debería ser demasiado doloroso con suerte.
Los archivos de memoria mapeados pueden no ser ideales para su situación (el modo de 32 bits aumenta la probabilidad de que no haya suficiente memoria virtual contigua, no pueda leerse desde tuberías u otros archivos que no sean archivos, etc.).
Aquí hay una solución que lee 128k bloques a la vez y siempre que su expresión regular coincida con una cadena más pequeña que ese tamaño, esto funcionará. También tenga en cuenta que no está restringido mediante el uso de expresiones regulares unifilares. Esta solución funciona bastante rápido, aunque sospecho que será un poco más lenta que usar mmap. Probablemente dependa más de lo que esté haciendo con las coincidencias, así como del tamaño/complejidad de la expresión regular que está buscando.
El método se asegurará de mantener un máximo de 2 bloques en la memoria. Es posible que desee aplicar al menos 1 partido por bloque como una comprobación de cordura en algunos casos de uso, pero este método se truncará para mantener el máximo de 2 bloques en la memoria. También se asegura de que NO se produzca ninguna coincidencia de expresiones regulares que consuma hasta el final del bloque actual y, en su lugar, se guarda la última posición para cuando la entrada verdadera se agote o tengamos otro bloque que corresponda a la expresión regular antes del final de, en para que coincida mejor con patrones como "[^ \ n] +" o "xxx $". Es posible que aún pueda romper cosas si tiene un vistazo al final de la expresión regular como xx (?! Xyz) donde yz está en el bloque siguiente, pero en la mayoría de los casos puede evitar el uso de dichos patrones.
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
Para probar/explorar, puede ejecutar esto:
# 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'))
Aquí está la salida:
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"
Esto puede ser útil en conjunción con rápido análisis de un gran XML donde se puede dividir en mini-DOMs basados en un elemento secundario como root, en lugar de tener que sumergirse en el manejo de devoluciones de llamadas y estados cuando se usa un analizador SAX. También le permite filtrar a través de XML más rápido también. Pero también lo he usado para miles de otros propósitos. ¡Estoy sorprendido de que recetas como esta no estén más disponibles en la red!
Una cosa más: El análisis en Unicode debe funcionar siempre que la transmisión aprobada produzca cadenas Unicode, y si está utilizando las clases de caracteres como \ w, deberá agregar el indicador re.U a la construcción del patrón re.compile. En este caso, block_size realmente significa recuento de caracteres en lugar de recuento de bytes.
¿Podría mostrar un ejemplo más completo de lo que quiere hacer? Con algunas entradas y salidas de muestra junto con él. –