txt - ejercicios de archivos en python
Búsqueda eficiente de la última línea en un archivo de texto (11)
¿Podría cargar el archivo en un mmap y luego usar mmap.rfind (string [, start [, end]]) para encontrar el segundo último carácter EOL en el archivo? Una búsqueda a ese punto en el archivo debería apuntarle a la última línea que pensaría.
Esta pregunta ya tiene una respuesta aquí:
- Obtén las últimas n líneas de un archivo con Python, similar a las respuestas de la cola 29
Necesito extraer la última línea de una serie de archivos de texto muy grandes (varios cientos de megabytes) para obtener ciertos datos. Actualmente, estoy usando python para recorrer todas las líneas hasta que el archivo esté vacío y luego procese la última línea devuelta, pero estoy seguro de que hay una forma más eficiente de hacerlo.
¿Cuál es la mejor manera de recuperar solo la última línea de un archivo de texto usando python?
Aquí hay una solución ligeramente diferente. En lugar de multilínea, me centré solo en la última línea, y en lugar de un tamaño de bloque constante, tengo un tamaño de bloque dinámico (doble). Ver comentarios para más información.
# Get last line of a text file using seek method. Works with non-constant block size.
# IDK if that speed things up, but it''s good enough for us,
# especially with constant line lengths in the file (provided by len_guess),
# in which case the block size doubling is not performed much if at all. Currently,
# we''re using this on a textfile format with constant line lengths.
# Requires that the file is opened up in binary mode. No nonzero end-rel seeks in text mode.
REL_FILE_END = 2
def lastTextFileLine(file, len_guess=1):
file.seek(-1, REL_FILE_END) # 1 => go back to position 0; -1 => 1 char back from end of file
text = file.read(1)
tot_sz = 1 # store total size so we know where to seek to next rel file end
if text != b''/n'': # if newline is the last character, we want the text right before it
file.seek(0, REL_FILE_END) # else, consider the text all the way at the end (after last newline)
tot_sz = 0
blocks = [] # For storing succesive search blocks, so that we don''t end up searching in the already searched
j = file.tell() # j = end pos
not_done = True
block_sz = len_guess
while not_done:
if j < block_sz: # in case our block doubling takes us past the start of the file (here j also = length of file remainder)
block_sz = j
not_done = False
tot_sz += block_sz
file.seek(-tot_sz, REL_FILE_END) # Yes, seek() works with negative numbers for seeking backward from file end
text = file.read(block_sz)
i = text.rfind(b''/n'')
if i != -1:
text = text[i+1:].join(reversed(blocks))
return str(text)
else:
blocks.append(text)
block_sz <<= 1 # double block size (converge with open ended binary search-like strategy)
j = j - block_sz # if this doesn''t work, try using tmp j1 = file.tell() above
return str(b''''.join(reversed(blocks))) # if newline was never found, return everything read
Lo ideal sería envolver esto en una clase LastTextFileLine y hacer un seguimiento de un promedio móvil de longitud de línea. Esto te daría una buena len_guess tal vez.
Busque hasta el final del archivo menos 100 bytes o menos. Haga una lectura y busque una nueva línea. Si aquí no hay una nueva línea, busque otros 100 bytes más o menos. Hacer espuma, enjuagar, repetir. Eventualmente encontrarás una nueva línea. La última línea comienza inmediatamente después de esa nueva línea.
En el mejor de los casos, solo hace una lectura de 100 bytes.
La ineficiencia aquí no se debe realmente a Python, sino a la naturaleza de cómo se leen los archivos. La única forma de encontrar la última línea es leer el archivo y encontrar los finales de línea. Sin embargo, la operación de búsqueda se puede usar para saltar a cualquier desplazamiento de bytes en el archivo. Por lo tanto, puede comenzar muy cerca del final del archivo, y agarrar trozos cada vez más grandes según sea necesario hasta que se encuentre el final de la última línea:
from os import SEEK_END
def get_last_line(file):
CHUNK_SIZE = 1024 # Would be good to make this the chunk size of the filesystem
last_line = ""
while True:
# We grab chunks from the end of the file towards the beginning until we
# get a new line
file.seek(-len(last_line) - CHUNK_SIZE, SEEK_END)
chunk = file.read(CHUNK_SIZE)
if not chunk:
# The whole file is one big line
return last_line
if not last_line and chunk.endswith(''/n''):
# Ignore the trailing newline at the end of the file (but include it
# in the output).
last_line = ''/n''
chunk = chunk[:-1]
nl_pos = chunk.rfind(''/n'')
# What''s being searched for will have to be modified if you are searching
# files with non-unix line endings.
last_line = chunk[nl_pos + 1:] + last_line
if nl_pos == -1:
# The whole chunk is part of the last line.
continue
return last_line
No es el camino directo, pero probablemente mucho más rápido que una simple implementación de Python:
line = subprocess.check_output([''tail'', ''-1'', filename])
Si puede elegir una longitud de línea máxima razonable, puede buscar casi el final del archivo antes de comenzar a leer.
myfile.seek(-max_line_length, os.SEEK_END)
line = myfile.readlines()[-1]
Si sabes la longitud máxima de una línea, puedes hacerlo
def getLastLine(fname, maxLineLength=80):
fp=file(fname, "rb")
fp.seek(-maxLineLength-1, 2) # 2 means "from the end of the file"
return fp.readlines()[-1]
Esto funciona en mi máquina de Windows. Pero no sé qué sucede en otras plataformas si abres un archivo de texto en modo binario. El modo binario es necesario si desea utilizar seek ().
Utilice el método de seek
del archivo con un desplazamiento negativo y whence=os.SEEK_END
para leer un bloque desde el final del archivo. Busca en el bloque los últimos caracteres de final de línea y toma todos los caracteres después de él. Si no hay un final de línea, retroceda más y repita el proceso.
def last_line(in_file, block_size=1024, ignore_ending_newline=False):
suffix = ""
in_file.seek(0, os.SEEK_END)
in_file_length = in_file.tell()
seek_offset = 0
while(-seek_offset < in_file_length):
# Read from end.
seek_offset -= block_size
if -seek_offset > in_file_length:
# Limit if we ran out of file (can''t seek backward from start).
block_size -= -seek_offset - in_file_length
if block_size == 0:
break
seek_offset = -in_file_length
in_file.seek(seek_offset, os.SEEK_END)
buf = in_file.read(block_size)
# Search for line end.
if ignore_ending_newline and seek_offset == -block_size and buf[-1] == ''/n'':
buf = buf[:-1]
pos = buf.rfind(''/n'')
if pos != -1:
# Found line end.
return buf[pos+1:] + suffix
suffix = buf + suffix
# One-line file.
return suffix
Tenga en cuenta que esto no funcionará en cosas que no admiten la seek
, como stdin o sockets. En esos casos, estás atascado leyendo todo (como lo hace el comando tail
).
#!/usr/bin/python
count = 0
f = open(''last_line1'',''r'')
for line in f.readlines():
line = line.strip()
count = count + 1
print line
print count
f.close()
count1 = 0
h = open(''last_line1'',''r'')
for line in h.readlines():
line = line.strip()
count1 = count1 + 1
if count1 == count:
print line #-------------------- this is the last line
h.close()
with open(''output.txt'', ''r'') as f:
lines = f.read().splitlines()
last_line = lines[-1]
print last_line
lines = file.readlines()
fileHandle.close()
last_line = lines[-1]