Como los datos es secuencial si el inicio y el final de la región de interés están cerca del principio del archivo, entonces la lectura desde el final del archivo (para encontrar el punto final coincidente) sigue siendo una mala solución.
He escrito un código que encontrará rápidamente los puntos de inicio y finalización que requiera, este enfoque se llama binary search y es similar al clásico juego de adivinanza "superior o inferior" para niños.
La secuencia de comandos lee una línea de prueba a medio camino entre lower_bounds
y upper_bounds
(inicialmente SOF y EOF), y comprueba los criterios de coincidencia. Si la línea buscada es anterior, entonces adivina de nuevo leyendo una línea a medio camino entre lower_bound
y la prueba de lectura anterior (si es más alta, se divide entre su conjetura y el límite superior). Así que sigues iterando entre los límites superior e inferior: esto produce la solución "promedio" más rápida posible.
Esto debería ser una solución realmente rápida (log a la base 2 de la cantidad de líneas !!). Por ejemplo, en el peor de los casos posibles (encontrar la línea 999 de entre 1000 líneas), usar la búsqueda binaria tomaría solo 9 lecturas de línea. (De mil millones de líneas tomaría sólo 30 ...)
Supuestos para el código de abajo:
- Cada línea comienza con la información de tiempo.
- Los tiempos son únicos. De lo contrario, cuando se encuentre una coincidencia, deberá verificar hacia atrás o hacia adelante para incluir o excluir todas las entradas con el tiempo coincidente, según corresponda (si es necesario).
- Curiosamente, esta es una función recursiva, por lo que el número de líneas de su archivo está limitado a 2 ** 1000 (afortunadamente, esto permite un archivo bastante grande ...)
Además:
- Este podría ser adaptado para leer en bloques arbitrarias, en vez de por línea, si se prefiere. Como lo sugirió J.F. Sebastian.
- En mi respuesta original, sugerí este enfoque pero usando linecache.getline, aunque esto es posible, no es apropiado para archivos grandes ya que lee todo el archivo en la memoria (así es
file.seek()
), gracias a TerryE y J.F. Sebastian por señalarlo.
de fecha y hora de importación
def match(line):
lfmt = '%Y-%m-%d %H:%M:%S'
if line[0] == '[':
return datetime.datetime.strptime(line[1:20], lfmt)
def retrieve_test_line(position):
file.seek(position,0)
file.readline() # avoids reading partial line, which will mess up match attempt
new_position = file.tell() # gets start of line position
return file.readline(), new_position
def check_lower_bound(position):
file.seek(position,0)
new_position = file.tell() # gets start of line position
return file.readline(), new_position
def find_line(target, lower_bound, upper_bound):
trial = int((lower_bound + upper_bound) /2)
inspection_text, position = retrieve_test_line(trial)
if position == upper_bound:
text, position = check_lower_bound(lower_bound)
if match(text) == target:
return position
return # no match for target within range
matched_position = match(inspection_text)
if matched_position == target:
return position
elif matched_position < target:
return find_line(target, position, upper_bound)
elif matched_position > target:
return find_line(target, lower_bound, position)
else:
return # no match for target within range
lfmt = '%Y-%m-%d %H:%M:%S'
# start_target = # first line you are trying to find:
start_target = datetime.datetime.strptime("2012-02-01 13:10:00", lfmt)
# end_target = # last line you are trying to find:
end_target = datetime.datetime.strptime("2012-02-01 13:39:00", lfmt)
file = open("log_file.txt","r")
lower_bound = 0
file.seek(0,2) # find upper bound
upper_bound = file.tell()
sequence_start = find_line(start_target, lower_bound, upper_bound)
if sequence_start or sequence_start == 0: #allow for starting at zero - corner case
sequence_end = find_line(end_target, sequence_start, upper_bound)
if not sequence_end:
print "start_target match: ", sequence_start
print "end match is not present in the current file"
else:
print "start match is not present in the current file"
if (sequence_start or sequence_start == 0) and sequence_end:
print "start_target match: ", sequence_start
print "end_target match: ", sequence_end
print
print start_target, 'target'
file.seek(sequence_start,0)
print file.readline()
print end_target, 'target'
file.seek(sequence_end,0)
print file.readline()
Se puede publicar sus archivos de registro? – kev
Haciendo caso omiso de la broma de kev, realmente debería mirar su política de rotación de archivos de registro. Es una mala práctica dejar que todos los archivos de registro se vuelvan tan grandes. – TerryE
La utilidad 'logrotate' es el camino a seguir aquí, existe para evitar este tipo de cosas. – Daenyth