Pregunta

¿Hay alguna alternativa al siguiente código?

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 archivo de texto enorme (~15MB) con líneas de longitud desconocida pero diferente, y necesito saltar a una línea en particular, ¿qué número sé de antemano? 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.

¿Fue útil?

Solución

linecache :

  

El linecache permite obtener cualquiera línea de un archivo fuente de Python, mientras intenta optimizar internamente, usando un caché, el caso común en el que se leen muchas líneas de un solo archivo. Este es utilizado por el traceback módulo para recuperar líneas de origen para incluir en el rastreo formateado ...

Otros consejos

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])

Realmente no tiene tantas opciones si las líneas son de diferente longitud ... lamentablemente necesita procesar los caracteres finales de línea para saber cuándo ha progresado a la siguiente línea.

Sin embargo, puede acelerar drásticamente esto Y reducir el uso de memoria cambiando el último parámetro a " abrir " a algo no 0.

0 significa que la operación de lectura de archivos no tiene búfer, lo cual es muy lento y requiere mucho disco. 1 significa que el archivo está en línea, lo que sería una mejora. Cualquier cosa por encima de 1 (digamos 8k .. es decir: 8096 o superior) lee fragmentos del archivo en la memoria. Aún puede acceder a través de for line in open(etc):, pero Python solo funciona de a poco, descartando cada fragmento almacenado en el búfer después de su procesamiento.

Probablemente estoy mimado por abundante carnero, 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.

Dado que 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 la línea de inicio. Todo lo que puedes hacer es hacer que se vea bien. Si el archivo es realmente enorme, entonces es posible que desee 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 es cero basado en este enfoque.

Me sorprende 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 desea todo el resto del archivo

rest_of_file = itertools.islice(Fhandle,index_of_interest)
for line in rest_of_file:
    print line

o si desea 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

Si conoce de antemano la posición en el archivo (en lugar del número de línea), puede usar file.seek () para ir a esa posición.

Editar : puede utilizar el linecache. función getline (filename, lineno) , que devolverá el contenido 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 el archivo (como Python puede querer hacer para imprimir un rastreo), pero no es bueno para un archivo de 15 MB.

Si no desea leer el archivo completo en la memoria ... es posible que deba crear algún formato que no sea texto sin formato.

por supuesto, todo depende de lo que intente hacer y con qué frecuencia saltará a través del archivo.

Por ejemplo, si vas a saltar a las líneas muchas veces en el mismo archivo, y sabes que el archivo no cambia mientras trabajas con él, puedes hacer esto:
Primero, pase el archivo completo y registre & Quot; seek-location & Quot; de algunos números de línea clave (como, cada 1000 líneas),
Luego, si desea la línea 12005, salte a la posición de 12000 (que ha grabado), luego lea 5 líneas y sabrá que está en la línea 12005 y así sucesivamente

¿Qué genera el archivo que desea procesar? Si es algo que está 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 (espacios rellenados o 0 números rellenados) y definitivamente será más pequeño. Y así se puede leer y procesar rápidamente.

  • ¿Qué línea quieres?
  • Calcular el desplazamiento de bytes del número de línea correspondiente en el archivo de índice (posible porque el tamaño de línea del archivo de índice es constante).
  • Use seek o lo que sea para saltar directamente para obtener la línea del archivo de índice.
  • Analizar para obtener el desplazamiento de bytes para la línea correspondiente del archivo real.

He tenido el mismo problema (necesito recuperarlo de una línea específica de gran archivo).

Seguramente, siempre puedo ejecutar todos los registros en el archivo y detenerlo cuando el contador sea igual a la línea de destino, pero no funciona de manera efectiva en un caso en el que desea obtener un número plural de filas específicas. Eso provocó la resolución del problema principal: cómo manejar directamente el lugar necesario del archivo.

Descubrí la siguiente decisión: En primer lugar, completé el diccionario con la posición inicial de cada línea (la clave es el número de línea y el valor & # 8211; 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

en última instancia, función de objetivo:

def give_line(line_number):
    t.seek(dict_pos.get(line_number))
    line = t.readline()
    return line

t.seek (número_línea) & # 8211; comando que ejecuta la poda del archivo hasta el inicio de la línea. Entonces, si luego confirma readline & # 8211; obtienes tu línea objetivo.

Utilizando este enfoque, he ahorrado una parte importante de tiempo.

¿Las líneas mismas contienen alguna información de índice? Si el contenido de cada línea era algo así como & Quot; <line index>:Data & Quot ;, entonces el enfoque seek() podría usarse para hacer una búsqueda binaria a través del archivo, incluso si la cantidad de Data es variable. Debería buscar el punto medio del archivo, leer 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 puede hacer es simplemente readlines(). Si no desea leer los 15 MB, puede usar el argumento sizehint para reemplazar al menos muchos readline() s con un número menor de llamadas a <=>.

Aquí hay un ejemplo usando '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)

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

Si se trata de un archivo de texto & amp; basado en 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)

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()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top