Leer solo líneas específicas

215

Estoy usando un bucle for para leer un archivo, pero solo quiero leer líneas específicas, por ejemplo, las líneas 26 y 30. ¿Hay alguna característica incorporada para lograr esto?

Gracias

eozzy
fuente
1
Posible dup: stackoverflow.com/questions/620367/…
Adam Matan

Respuestas:

253

Si el archivo a leer es grande y no desea leer todo el archivo en la memoria de una vez:

fp = open("file")
for i, line in enumerate(fp):
    if i == 25:
        # 26th line
    elif i == 29:
        # 30th line
    elif i > 29:
        break
fp.close()

Tenga en cuenta que i == n-1para la nlínea th.


En Python 2.6 o posterior:

with open("file") as fp:
    for i, line in enumerate(fp):
        if i == 25:
            # 26th line
        elif i == 29:
            # 30th line
        elif i > 29:
            break
Alok Singhal
fuente
8
enumerate(x)utiliza x.next, por lo que no necesita todo el archivo en la memoria.
Alok Singhal
3
Mi pequeño problema con esto es que A) Quieres usarlo en lugar del par abierto / cerrado y así mantener el cuerpo corto, B) Pero el cuerpo no es tan corto. Suena como una compensación entre velocidad / espacio y ser Pythonic. No estoy seguro de cuál sería la mejor solución.
Hamish Grubijan
55
con está sobrevalorado, Python se llevó bien durante más de 13 años sin él
Dan D.
38
@Dan D. La electricidad está sobrevalorada, la humanidad se llevó bien durante más de 200 mil años sin ella. ;-) 'with' lo hace más seguro, más legible y una línea más corta.
Romain Vincent
99
por qué usar for loop, no creo que entiendas el significado de big file. El ciclo tardará años en llegar al índice
devssh
159

La respuesta rápida:

f=open('filename')
lines=f.readlines()
print lines[25]
print lines[29]

o:

lines=[25, 29]
i=0
f=open('filename')
for line in f:
    if i in lines:
        print i
    i+=1

Hay una solución más elegante para extraer muchas líneas: linecache (cortesía de "python: ¿cómo saltar a una línea particular en un archivo de texto enorme?" , Una pregunta anterior de stackoverflow.com).

Citando la documentación de Python vinculada anteriormente:

>>> import linecache
>>> linecache.getline('/etc/passwd', 4)
'sys:x:3:3:sys:/dev:/bin/sh\n'

Cambiar el 4 a su número de línea deseado, y listo. Tenga en cuenta que 4 traería la quinta línea ya que el conteo se basa en cero.

Si el archivo puede ser muy grande y causar problemas al leerlo en la memoria, puede ser una buena idea tomar el consejo de @ Alok y usar enumerate () .

Para concluir:

  • Use fileobject.readlines()o for line in fileobjectcomo una solución rápida para archivos pequeños.
  • Úselo linecachepara una solución más elegante, que será bastante rápida para leer muchos archivos, posible repetidamente.
  • Siga los consejos de @ Alok y utilícelosenumerate() para archivos que pueden ser muy grandes y que no caben en la memoria. Tenga en cuenta que el uso de este método puede disminuir porque el archivo se lee secuencialmente.
Adam Matan
fuente
77
Agradable. Acabo de mirar la fuente del linecachemódulo y parece que lee todo el archivo en la memoria. Entonces, si el acceso aleatorio es más importante que la optimización de tamaño, linecachees el mejor método.
Alok Singhal
77
con linecache.getlin ('some_file', 4) obtengo la cuarta línea, no la quinta.
Juan
Dato curioso: si utiliza un conjunto en lugar de la lista en el segundo ejemplo, obtendrá O (1) tiempo de ejecución. Buscar en una lista es O (n). Los conjuntos internos se representan como hashes, y es por eso que obtienes el tiempo de ejecución O (1). no es un gran problema en este ejemplo, pero si usa una lista grande de números y le preocupa la eficiencia, entonces los conjuntos son el camino a seguir.
rady
linecacheahora parece que solo funciona para los archivos fuente de Python
Paul H
También puede usar linecache.getlines('/etc/passwd')[0:4]para leer en la primera, segunda, tercera y cuarta línea.
zyy
30

Un enfoque rápido y compacto podría ser:

def picklines(thefile, whatlines):
  return [x for i, x in enumerate(thefile) if i in whatlines]

esto acepta cualquier objeto abierto similar a un archivo thefile(dejando a la persona que llama si debe abrirse desde un archivo de disco, o por ejemplo, a través de un socket u otra secuencia similar a un archivo) y un conjunto de índices de línea basados ​​en cero whatlines, y devuelve un lista, con poca huella de memoria y velocidad razonable. Si el número de líneas a devolver es enorme, es posible que prefiera un generador:

def yieldlines(thefile, whatlines):
  return (x for i, x in enumerate(thefile) if i in whatlines)

que básicamente solo es bueno para hacer un bucle: tenga en cuenta que la única diferencia proviene del uso de paréntesis redondeados en lugar de cuadrados en la returndeclaración, haciendo una comprensión de lista y una expresión generadora respectivamente.

Además, tenga en cuenta que, a pesar de la mención de "líneas" y "archivo", estas funciones son mucho, mucho más generales: funcionarán en cualquier iterable, ya sea un archivo abierto o cualquier otro, devolviendo una lista (o generador) de elementos basado en sus números de artículo progresivos. Por lo tanto, sugeriría usar nombres generales más apropiados ;-).

Alex Martelli
fuente
@ephemient, no estoy de acuerdo: el genexp se lee sin problemas y perfectamente.
Alex Martelli
Excelente y elegante solución, gracias! De hecho, incluso los archivos grandes deben ser compatibles, con la expresión del generador. No puede ser más elegante que esto, ¿verdad? :)
Samuel Lampa
Buena solución, ¿cómo se compara esto con la propuesta por @AdamMatan? La solución de Adam podría ser más rápida ya que explota información adicional (los números de línea aumentan monótonamente) lo que podría conducir a una parada temprana. Tengo un archivo de 10GB que no puedo cargar en la memoria.
Mannaggia
2
@Mannaggia No se enfatiza lo suficiente en esta respuesta, pero whatlinesdebería ser una set, porque if i in whatlinesse ejecutará más rápido con un conjunto en lugar de una lista (ordenada). No lo noté primero y, en su lugar, ideé mi propia solución fea con una lista ordenada (donde no tenía que escanear una lista cada vez, mientras if i in whatlineslo hacía), pero la diferencia en el rendimiento fue insignificante (con mis datos) y esto La solución es mucho más elegante.
Victor K
28

En aras de ofrecer otra solución:

import linecache
linecache.getline('Sample.txt', Number_of_Line)

Espero que esto sea rápido y fácil :)

KingMak
fuente
1
Espero que esta sea la solución más óptima.
maniac_user
2
Esto lee todo el archivo en la memoria. También podría llamar a file.read (). Split ('\ n') y luego usar búsquedas de índice de matriz para obtener la línea de interés ...
duhaime
¿Podría dar un ejemplo @duhaime
Anon
14

si quieres la linea 7

line = open ("file.txt", "r"). readlines () [7]
MadSc13ntist
fuente
14
Ordenado. Pero, ¿cómo haces close()el archivo al abrirlo de esta manera?
Milo Wielondek
1
@ 0sh ¿necesitamos cerrar?
Ooker
1
si. necesitamos cerrar después de esto. Cuando abrimos un archivo usando "con" ... se cierra solo.
reetesh11
10

En aras de la exhaustividad, aquí hay una opción más.

Comencemos con una definición de documentos de Python :

rebanada Un objeto que generalmente contiene una porción de una secuencia. Se crea un segmento utilizando la notación de subíndice, [] con dos puntos entre los números cuando se dan varios, como en variable_name [1: 3: 5]. La notación de corchete (subíndice) usa objetos de división internamente (o en versiones anteriores, __getslice __ () y __setslice __ ()).

Aunque la notación de corte no es directamente aplicable a los iteradores en general, el itertoolspaquete contiene una función de reemplazo:

from itertools import islice

# print the 100th line
with open('the_file') as lines:
    for line in islice(lines, 99, 100):
        print line

# print each third line until 100
with open('the_file') as lines:
    for line in islice(lines, 0, 100, 3):
        print line

La ventaja adicional de la función es que no lee el iterador hasta el final. Entonces puedes hacer cosas más complejas:

with open('the_file') as lines:
    # print the first 100 lines
    for line in islice(lines, 100):
        print line

    # then skip the next 5
    for line in islice(lines, 5):
        pass

    # print the rest
    for line in lines:
        print line

Y para responder la pregunta original:

# how to read lines #26 and #30
In [365]: list(islice(xrange(1,100), 25, 30, 4))
Out[365]: [26, 30]
nuevo
fuente
1
Con mucho, el mejor enfoque cuando se trabaja con archivos grandes. Mi programa pasó de consumir 8GB + a casi nada. El cambio fue el uso de la CPU que pasó de ~ 15% a ~ 40%, pero el procesamiento real del archivo fue un 70% más rápido. Tomaré ese tradoff todo el día. ¡Gracias! 🎉🎉🎉
GollyJer
1
Esto me parece lo más pitónico. ¡Gracias!
ipetrik
10

Leer archivos es increíblemente rápido. Leer un archivo de 100 MB lleva menos de 0.1 segundos (vea mi artículo Leer y escribir archivos con Python ). Por lo tanto, debe leerlo completamente y luego trabajar con las líneas individuales.

Lo que la mayoría de las respuestas hacen aquí no es incorrecto, sino un mal estilo. La apertura de archivos siempre debe realizarse withya que se asegura de que el archivo se cierre nuevamente.

Entonces deberías hacerlo así:

with open("path/to/file.txt") as f:
    lines = f.readlines()
print(lines[26])  # or whatever you want to do with this line
print(lines[30])  # or whatever you want to do with this line

Archivos enormes

Si tiene un gran archivo y el consumo de memoria es una preocupación, puede procesarlo línea por línea:

with open("path/to/file.txt") as f:
    for i, line in enumerate(f):
        pass  # process line i
Martin Thoma
fuente
En mi opinión, es un estilo realmente malo leer un archivo completo de longitud desconocida, solo para obtener las primeras 30 líneas ... ¿qué es sobre el consumo de memoria ... y qué es sobre las secuencias sin fin?
return42
@ return42 Depende mucho de la aplicación. Para muchos, está muy bien suponer que un archivo de texto tiene un tamaño mucho menor que la memoria disponible. Si tiene archivos potencialmente grandes, he editado mi respuesta.
Martin Thoma
gracias por su adición, que es lo mismo que alok answer . Y lo siento, no, no creo que esto dependa de la aplicación. En mi opinión, siempre es mejor no leer más líneas de las que necesita.
regreso42
7

Algunos de estos son encantadores, pero se puede hacer mucho más simple:

start = 0 # some starting index
end = 5000 # some ending index
filename = 'test.txt' # some file we want to use

with open(filename) as fh:
    data = fin.readlines()[start:end]

print(data)

Eso usará simplemente el corte de listas, carga todo el archivo, pero la mayoría de los sistemas minimizarán el uso de memoria de manera apropiada, es más rápido que la mayoría de los métodos anteriores y funciona en mis archivos de datos 10G +. ¡Buena suerte!

Será
fuente
4

Puede hacer una llamada a seek () que posiciona su cabeza de lectura en un byte especificado dentro del archivo. Esto no lo ayudará a menos que sepa exactamente cuántos bytes (caracteres) están escritos en el archivo antes de la línea que desea leer. Quizás su archivo esté estrictamente formateado (¿cada línea tiene un número X de bytes?) O puede contar el número de caracteres usted mismo (recuerde incluir caracteres invisibles como saltos de línea) si realmente desea aumentar la velocidad.

De lo contrario, debe leer cada línea antes de la línea que desee, según una de las muchas soluciones que ya se proponen aquí.

romano
fuente
3

Si su archivo de texto grande fileestá estrictamente bien estructurado (lo que significa que cada línea tiene la misma longitud l), puede usar para la nlínea -th

with open(file) as f:
    f.seek(n*l)
    line = f.readline() 
    last_pos = f.tell()

Descargo de responsabilidad ¡ Esto solo funciona para archivos con la misma longitud!

Michael Dorner
fuente
2

Qué tal esto:

>>> with open('a', 'r') as fin: lines = fin.readlines()
>>> for i, line in enumerate(lines):
      if i > 30: break
      if i == 26: dox()
      if i == 30: doy()
Hamish Grubijan
fuente
Es cierto, esto es menos eficiente que el de Alok, pero el mío usa un con declaración;)
Hamish Grubijan
2

Si no le importa importar, fileinput hace exactamente lo que necesita (es decir, puede leer el número de línea de la línea actual)

ennuikiller
fuente
2
def getitems(iterable, items):
  items = list(items) # get a list from any iterable and make our own copy
                      # since we modify it
  if items:
    items.sort()
    for n, v in enumerate(iterable):
      if n == items[0]:
        yield v
        items.pop(0)
        if not items:
          break

print list(getitems(open("/usr/share/dict/words"), [25, 29]))
# ['Abelson\n', 'Abernathy\n']
# note that index 25 is the 26th item

fuente
Roger, mi chico favorito! Esto podría beneficiarse de una declaración con.
Hamish Grubijan
2

Prefiero este enfoque porque es más general, es decir, puede usarlo en un archivo, en el resultado de f.readlines(), en un StringIOobjeto, lo que sea:

def read_specific_lines(file, lines_to_read):
   """file is any iterable; lines_to_read is an iterable containing int values"""
   lines = set(lines_to_read)
   last = max(lines)
   for n, line in enumerate(file):
      if n + 1 in lines:
          yield line
      if n + 1 > last:
          return

>>> with open(r'c:\temp\words.txt') as f:
        [s for s in read_specific_lines(f, [1, 2, 3, 1000])]
['A\n', 'a\n', 'aa\n', 'accordant\n']
Robert Rossney
fuente
2

Aquí están mis pequeños 2 centavos, por lo que vale;)

def indexLines(filename, lines=[2,4,6,8,10,12,3,5,7,1]):
    fp   = open(filename, "r")
    src  = fp.readlines()
    data = [(index, line) for index, line in enumerate(src) if index in lines]
    fp.close()
    return data


# Usage below
filename = "C:\\Your\\Path\\And\\Filename.txt"
for line in indexLines(filename): # using default list, specify your own list of lines otherwise
    print "Line: %s\nData: %s\n" % (line[0], line[1])
AWainb
fuente
2

Un cambio mejor y menor para la respuesta de Alok Singhal

fp = open("file")
for i, line in enumerate(fp,1):
    if i == 26:
        # 26th line
    elif i == 30:
        # 30th line
    elif i > 30:
        break
fp.close()
sedic
fuente
1

@OP, puedes usar enumerate

for n,line in enumerate(open("file")):
    if n+1 in [26,30]: # or n in [25,29] 
       print line.rstrip()
ghostdog74
fuente
1
file = '/path/to/file_to_be_read.txt'
with open(file) as f:
    print f.readlines()[26]
    print f.readlines()[30]

Usando la instrucción with, esto abre el archivo, imprime las líneas 26 y 30 y luego cierra el archivo. ¡Sencillo!

usuario3901273
fuente
Esta no es una respuesta válida. después de que la primera llamada al readlines()iterador se agote y la segunda llamada devuelva una lista vacía o arroje un error (no recuerdo cuál)
Paul H
1

Puede hacerlo de manera muy simple con esta sintaxis que alguien ya mencionó, pero es, con mucho, la forma más fácil de hacerlo:

inputFile = open("lineNumbers.txt", "r")
lines = inputFile.readlines()
print (lines[0])
print (lines[2])
Trey50Daniel
fuente
1

Para imprimir la línea n. ° 3,

line_number = 3

with open(filename,"r") as file:
current_line = 1
for line in file:
    if current_line == line_number:
        print(file.readline())
        break
    current_line += 1

Autor original: Frank Hofmann

narcisos locos
fuente
1

Bastante rápido y al grano.

Para imprimir ciertas líneas en un archivo de texto. Cree una lista "lines2print" y luego simplemente imprima cuando la enumeración esté "en" la lista lines2print. Para deshacerse de '\ n' extra, use line.strip () o line.strip ('\ n'). Simplemente me gusta "lista de comprensión" y trato de usar cuando puedo. Me gusta el método "con" para leer archivos de texto para evitar dejar un archivo abierto por cualquier motivo.

lines2print = [26,30] # can be a big list and order doesn't matter.

with open("filepath", 'r') as fp:
    [print(x.strip()) for ei,x in enumerate(fp) if ei in lines2print]

o si la lista es pequeña, simplemente escriba la lista como una lista en la comprensión.

with open("filepath", 'r') as fp:
    [print(x.strip()) for ei,x in enumerate(fp) if ei in [26,30]]
Mike Adrion
fuente
0

Para imprimir la línea deseada. Para imprimir la línea por encima / debajo de la línea requerida.

def dline(file,no,add_sub=0):
    tf=open(file)
    for sno,line in enumerate(tf):
        if sno==no-1+add_sub:
         print(line)
    tf.close()

ejecutar ----> dline ("D: \ dummy.txt", 6) es decir, dline ("ruta del archivo", número_línea, si desea que la línea superior de la línea buscada dé 1 para el -1 inferior, este es el valor predeterminado opcional ser tomado 0)

sudhir tataraju
fuente
0

Si desea leer líneas específicas, como la línea que comienza después de alguna línea de umbral, puede usar los siguientes códigos, file = open("files.txt","r") lines = file.readlines() ## convert to list of lines datas = lines[11:] ## raed the specific lines

Niharranjan Pradhan
fuente
-1
f = open(filename, 'r')
totalLines = len(f.readlines())
f.close()
f = open(filename, 'r')

lineno = 1
while lineno < totalLines:
    line = f.readline()

    if lineno == 26:
        doLine26Commmand(line)

    elif lineno == 30:
        doLine30Commmand(line)

    lineno += 1
f.close()
inspectorG4dget
fuente
77
Esto es tan antipónico como se pone.
SilentGhost
Da el resultado incorrecto, ya que no puede usar líneas de lectura y líneas de lectura así (cada una cambia la posición de lectura actual).
Lamento haber pasado por alto un error ENORME en mi primer código. El error se ha corregido y el código actual debería funcionar como se esperaba. Gracias por señalar mi error, Roger Pate.
inspectorG4dget
-1

Creo que esto funcionaria

 open_file1 = open("E:\\test.txt",'r')
 read_it1 = open_file1.read()
 myline1 = []
 for line1 in read_it1.splitlines():
 myline1.append(line1)
 print myline1[0]
San k
fuente
Ya había una docena de métodos de línea de lectura cuando publicó esto, agregar otro solo agrega desorden
duhaime