python - txt - ¿Cómo saltar a una línea particular en un gran archivo de texto?
retorno de carro python (15)
¿Hay alguna alternativa al código a continuación?
startFromLine = 141978 # or whatever line I need to jump to
urlsfile = open(filename, "rb", 0)
linesCounter = 1
for line in urlsfile:
if linesCounter > startFromLine:
DoSomethingWithThisLine(line)
linesCounter += 1
Si estoy procesando un gran archivo de texto (~15MB)
con líneas de longitud desconocida pero diferente, y necesito saltar a una línea en particular, ¿qué número sé con antelación? Me siento mal al procesarlos uno por uno cuando sé que podría ignorar al menos la primera mitad del archivo. Buscando una solución más elegante si hay alguna.
¿Las líneas mismas contienen información de índice? Si el contenido de cada línea fuera algo así como " <line index>:Data
", entonces el enfoque de seek()
podría utilizarse para realizar una búsqueda binaria a través del archivo, incluso si la cantidad de Data
es variable. Buscaría en el punto medio del archivo, leería una línea, verificará si su índice es más alto o más bajo que el que desea, etc.
De lo contrario, lo mejor que puedes hacer es readlines()
. Si no desea leer todos los 15MB, puede usar el argumento sizehint
para al menos reemplazar una gran cantidad de readline()
con un número menor de llamadas a readlines()
.
¿Qué genera el archivo que desea procesar? Si se trata de algo bajo su control, podría generar un índice (qué línea está en qué posición) en el momento en que se agrega el archivo. El archivo de índice puede tener un tamaño de línea fijo (espacio acolchado o 0 números acolchados) y definitivamente será más pequeño. Y, por lo tanto, puede leerse y procesarse qucikly.
- ¿Qué línea quieres?
- Calcule el desplazamiento de bytes del número de línea correspondiente en el archivo de índice (es posible porque el tamaño de línea del archivo de índice es constante).
- Utilice seek o lo que sea para saltar directamente para obtener la línea del archivo de índice.
- Parse para obtener el desplazamiento de bytes para la línea correspondiente del archivo real.
Aquí hay un ejemplo que usa ''readlines (sizehint)'' para leer un trozo de líneas a la vez. DNS señaló esa solución. Escribí este ejemplo porque los otros ejemplos aquí están orientados a una sola línea.
def getlineno(filename, lineno):
if lineno < 1:
raise TypeError("First line is line 1")
f = open(filename)
lines_read = 0
while 1:
lines = f.readlines(100000)
if not lines:
return None
if lines_read + len(lines) >= lineno:
return lines[lineno-lines_read-1]
lines_read += len(lines)
print getlineno("nci_09425001_09450000.smi", 12000)
Como no hay forma de determinar la longitud de todas las líneas sin leerlas, no tiene más remedio que iterar sobre todas las líneas antes de su línea de partida. Todo lo que puedes hacer es hacer que se vea bien. Si el archivo es realmente grande, puede utilizar un enfoque basado en generador:
from itertools import dropwhile
def iterate_from_line(f, start_from_line):
return (l for i, l in dropwhile(lambda x: x[0] < start_from_line, enumerate(f)))
for line in iterate_from_line(open(filename, "r", 0), 141978):
DoSomethingWithThisLine(line)
Nota: el índice está basado en cero en este enfoque.
Estoy sorprendido de que nadie haya mencionado a islice
line = next(itertools.islice(Fhandle,index_of_interest,index_of_interest+1),None) # just the one line
o si quieres el resto del archivo
rest_of_file = itertools.islice(Fhandle,index_of_interest)
for line in rest_of_file:
print line
o si quieres cualquier otra línea del archivo
rest_of_file = itertools.islice(Fhandle,index_of_interest,None,2)
for odd_line in rest_of_file:
print odd_line
He tenido el mismo problema (necesito recuperarlo de una línea específica de archivo enorme).
Seguramente, puedo ejecutar todos los registros en un archivo y detenerlo cuando el contador será igual a la línea objetivo, pero no funciona de manera efectiva en un caso en el que desea obtener un número plural de filas específicas. Eso causó que el problema principal se resolviera: cómo manejarlo directamente en el lugar de archivo necesario.
Descubrí la siguiente decisión: primero, terminé el diccionario con la posición inicial de cada línea (la clave es el número de línea y el valor, la longitud acumulada de las líneas anteriores).
t = open(file,’r’)
dict_pos = {}
kolvo = 0
length = 0
for each in t:
dict_pos[kolvo] = length
length = length+len(each)
kolvo = kolvo+1
finalmente, función de objetivo:
def give_line(line_number):
t.seek(dict_pos.get(line_number))
line = t.readline()
return line
t.seek (line_number) - comando que ejecuta la poda del archivo hasta el inicio de la línea. Por lo tanto, si luego comprometes readline, obtienes tu línea objetivo.
Usando tal enfoque, he ahorrado una parte importante del tiempo.
No puede avanzar sin leer el archivo al menos una vez, ya que no sabe dónde están los saltos de línea. Podrías hacer algo como:
# Read in the file once and build a list of line offsets
line_offset = []
offset = 0
for line in file:
line_offset.append(offset)
offset += len(line)
file.seek(0)
# Now, to skip to line n (with the first line being line 0), just do
file.seek(line_offset[n])
Probablemente estoy echado a perder por la abundancia de ram, pero 15 M no es enorme. Leer en la memoria con readlines()
es lo que suelo hacer con archivos de este tamaño. Acceder a una línea después de eso es trivial.
Puede usar esta función para devolver la línea n:
def skipton(infile, n):
with open(infile,''r'') as fi:
for i in range(n-1):
fi.next()
return fi.next()
Puede usar mmap para encontrar el desplazamiento de las líneas. MMap parece ser la forma más rápida de procesar un archivo
ejemplo:
with open(''input_file'', "r+b") as f:
mapped = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
i = 1
for line in iter(mapped.readline, ""):
if i == Line_I_want_to_jump:
offsets = mapped.tell()
i+=1
luego use f.seek (offsets) para moverse a la línea que necesita
Realmente no tienes tantas opciones si las líneas son de diferente longitud ... lamentablemente necesitas procesar los caracteres de final de línea para saber cuándo has progresado a la siguiente línea.
Sin embargo, puede acelerar drásticamente y reducir el uso de la memoria cambiando el último parámetro a "abrir" a algo que no sea 0.
0 significa que la operación de lectura de archivos no está búfer, lo que es muy lento y requiere mucho disco. 1 significa que el archivo está almacenado en línea, lo que sería una mejora. Cualquier cosa superior a 1 (digamos 8k ... es decir, 8096 o superior) lee fragmentos del archivo en la memoria. Todavía tiene acceso a través for line in open(etc):
pero Python solo va un poco a la vez, descartando cada fragmento almacenado después de procesado.
Si conoce de antemano la posición en el archivo (más bien el número de línea), puede usar file.seek() para ir a esa posición.
Editar : puede usar la función linecache.getline (filename, lineno) , que devolverá los contenidos de la línea lineno, pero solo después de leer todo el archivo en la memoria. Es bueno si está accediendo aleatoriamente a líneas desde dentro del archivo (como python podría querer hacer para imprimir un rastreo) pero no es bueno para un archivo de 15 MB.
Si está trabajando con un archivo de texto y está basado en el sistema Linux , puede usar los comandos de Linux.
¡Para mí, esto funcionó bien!
import commands
def read_line(path, line=1):
return commands.getoutput(''head -%s %s | tail -1'' % (line, path))
line_to_jump = 141978
read_line("path_to_large_text_file", line_to_jump)
Si no desea leer todo el archivo en la memoria ... puede que necesite encontrar algún formato que no sea texto sin formato.
por supuesto, todo depende de lo que intentes hacer y de la frecuencia con la que saltarás por el archivo.
Por ejemplo, si va a saltar a líneas muchas veces en el mismo archivo, y sabe que el archivo no cambia mientras trabaja con él, puede hacer esto:
Primero, pase por todo el archivo y grabe la "ubicación de búsqueda" de algunos números de línea clave (como, por ejemplo, 1000 líneas),
Luego, si quieres la línea 12005, salta a la posición de 12000 (que has grabado) y luego lee 5 líneas y sabrás que estás en la línea 12005 y así sucesivamente
El módulo de
linecache
permite a uno obtener cualquier línea de un archivo fuente de Python, mientras intenta optimizar internamente, usando un caché, el caso común donde se leen muchas líneas de un solo archivo. Esto es utilizado por el módulo detraceback
para recuperar las líneas de origen para su inclusión en el rastreo con formato ...