¿Cómo estoy?buscar o re.partido en un archivo completo sin leer todo en la memoria?


Quiero ser capaz de ejecutar una expresión regular en un archivo completo, pero me gustaría ser capaz de no tener que leer todo el archivo en la memoria a la vez, ya que puede estar trabajando con archivos bastante grandes en el futuro. ¿Hay alguna manera de hacer esto? ¡Gracias!

Aclaración: No puedo leer línea por línea porque puede abarcar varias líneas.

Author: Mark Harrison, 2009-01-18

7 answers

Puede usar mmap para asignar el archivo a la memoria. Se puede acceder al contenido del archivo como una cadena normal:

import re, mmap

with open('/var/log/error.log', 'r+') as f:
  data = mmap.mmap(f.fileno(), 0)
  mo = re.search('error: (.*)', data)
  if mo:
    print "found error", mo.group(1)

Esto también funciona para archivos grandes, el contenido del archivo se carga internamente desde el disco según sea necesario.

 49
Author: sth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-18 03:24:46

Esto depende del archivo y la expresión regular. Lo mejor que podría hacer sería leer el archivo línea por línea, pero si eso no funciona para su situación, entonces podría quedarse atascado con tirar de todo el archivo en la memoria.

Digamos por ejemplo que este es tu archivo:

Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Ut fringilla pede blandit
eros sagittis viverra. Curabitur facilisis
urna ABC elementum lacus molestie aliquet.
Vestibulum lobortis semper risus. Etiam
sollicitudin. Vivamus posuere mauris eu
nulla. Nunc nisi. Curabitur fringilla fringilla
elit. Nullam feugiat, metus et suscipit
fermentum, mauris ipsum blandit purus,
non vehicula purus felis sit amet tortor.
Vestibulum odio. Mauris dapibus ultricies
metus. Cras XYZ eu lectus. Cras elit turpis,
ultrices nec, commodo eu, sodales non, erat.
Quisque accumsan, nunc nec porttitor vulputate,
erat dolor suscipit quam, a tristique justo
turpis at erat.

Y esta fue tu expresión regular:

consectetur(?=\sadipiscing)

Ahora esta expresión regular usa lookahead positivo y solo coincidirá con una cadena de "consectetur" si es seguida inmediatamente por cualquier carácter de espacio en blanco y luego una cadena de "adipiscing".

Así que en este ejemplo tendría que leer todo el archivo en memoria porque su expresión regular depende de que todo el archivo se analice como una sola cadena. Este es uno de los muchos ejemplos que requerirían tener toda la cadena en memoria para que funcione una expresión regular en particular.

Supongo que la respuesta desafortunada es que todo depende de su situación.

 5
Author: Andrew Hare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-18 01:42:26

Si esto es un gran problema y vale la pena un poco de esfuerzo, puede convertir la expresión regular en una máquina de estados finitos que lea el archivo. El FSM puede ser de complejidad O (n), lo que significa que será mucho más rápido a medida que el tamaño del archivo se haga grande.

Podrá hacer coincidir de manera eficiente patrones que abarcan líneas en archivos demasiado grandes para caber en la memoria.

Aquí hay dos lugares que describen el algoritmo para convertir una expresión regular a FSM:

 2
Author: Mark Harrison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-18 02:39:06

Esta es una manera:

import re

REGEX = '\d+'

with open('/tmp/workfile', 'r') as f:
      for line in f:
          print re.match(REGEX,line)
  1. con operador en python 2.5 tomas de cierre automático de archivos. Por lo tanto, no necesita preocuparse por ello.
  2. el iterador sobre el objeto file es eficiente en memoria. es decir, no leerá más que una línea de memoria en un momento dado.
  3. Pero la desventaja de este enfoque es que tomaría mucho tiempo para archivos enormes.

Otro enfoque que me viene a la mente es usar read(size) y file.método seek (offset), que leerá un parte del tamaño del archivo a la vez.

import re

REGEX = '\d+'

with open('/tmp/workfile', 'r') as f:
      filesize = f.size()
      part = filesize / 10 # a suitable size that you can determine ahead or in the prog.
      position = 0 
      while position <= filesize: 
          content = f.read(part)
          print re.match(REGEX,content)
          position = position + part
          f.seek(position)

También puede combinar estos dos allí puede crear un generador que devolvería el contenido de un cierto bytes en el momento e iterar a través de ese contenido para comprobar su expresión regular. Esta OMI sería un buen enfoque.

 1
Author: Senthil Kumaran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-09 22:41:47

Para los patrones de una sola línea puede iterar sobre las líneas del archivo, pero para los patrones de varias líneas, Tendrá que leer todo (o parte, pero será difícil realizar un seguimiento) del archivo en la memoria.

 0
Author: sykora,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-18 01:44:49

Abra el archivo e itere sobre las líneas.

fd = open('myfile')
for line in fd:
    if re.match(...,line)
        print line
 0
Author: Mark Harrison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-18 01:46:43
f = open(filename,'r')
  for eachline in f:
    string=re.search("(<tr align=\"right\"><td>)([0-9]*)(</td><td>)([a-zA-Z]*)(</td><td>)([a-zA-Z]*)(</td>)",eachline)
    if string:
      for i in range (2,8,2):
        add = string.group(i)
        l.append(add)
 0
Author: Atul Dhingra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-18 13:56:39