Nur bestimmte Zeilen lesen

215

Ich verwende eine for-Schleife, um eine Datei zu lesen, aber ich möchte nur bestimmte Zeilen lesen, z. B. Zeile 26 und 30. Gibt es eine eingebaute Funktion, um dies zu erreichen?

Vielen Dank

eozzy
quelle
1
Mögliches dup: stackoverflow.com/questions/620367/…
Adam Matan

Antworten:

253

Wenn die zu lesende Datei groß ist und Sie nicht die gesamte Datei auf einmal lesen möchten:

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

Beachten Sie dies i == n-1für die ndritte Zeile.


In Python 2.6 oder höher:

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
quelle
8
enumerate(x)verwendet x.next, so dass nicht die gesamte Datei im Speicher benötigt wird.
Alok Singhal
3
Mein kleines Rindfleisch dabei ist, dass A) Sie anstelle des offenen / geschlossenen Paares verwenden möchten und somit den Körper kurz halten, B) aber der Körper nicht so kurz ist. Klingt nach einem Kompromiss zwischen Geschwindigkeit / Raum und Pythonic. Ich bin mir nicht sicher, was die beste Lösung wäre.
Hamish Grubijan
5
mit ist überbewertet, Python kam über 13 Jahre ohne gut aus
Dan D.
38
@ Dan D. Strom ist überbewertet, die Menschheit hat sich über 200.000 Jahre ohne ihn gut verstanden. ;-) 'with' macht es sicherer, lesbarer und eine Zeile kürzer.
Romain Vincent
9
Warum for-Schleife verwenden? Ich glaube nicht, dass Sie die Bedeutung von verstehen big file. Die Schleife wird Jahre dauern, um den Index zu erreichen
devssh
159

Die schnelle Antwort:

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

oder:

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

Es gibt eine elegantere Lösung zum Extrahieren vieler Zeilen: Zeilencache (mit freundlicher Genehmigung von "Python: Wie springe ich zu einer bestimmten Zeile in einer riesigen Textdatei?" , Eine frühere Frage von stackoverflow.com).

Zitieren der oben verlinkten Python-Dokumentation:

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

Ändern Sie die 4auf die gewünschte Zeilennummer und schon sind Sie dran. Beachten Sie, dass 4 die fünfte Zeile bringen würde, da die Zählung auf Null basiert.

Wenn die Datei sehr groß ist und Probleme beim Einlesen in den Speicher verursacht, ist es möglicherweise eine gute Idee, den Rat von @ Alok zu befolgen und enumerate () zu verwenden .

Schlussfolgern:

  • Verwenden Sie fileobject.readlines()oder for line in fileobjectals schnelle Lösung für kleine Dateien.
  • Verwenden Sie linecachefür eine elegantere Lösung, die zum Lesen vieler Dateien sehr schnell ist und wiederholt möglich ist.
  • Nehmen @ Alok Rat und Verwendungenumerate() für Dateien , die sehr groß sein können, und werden nicht in den Speicher passen. Beachten Sie, dass die Verwendung dieser Methode möglicherweise langsamer wird, da die Datei nacheinander gelesen wird.
Adam Matan
quelle
7
Nett. Ich habe mir nur die Quelle des linecacheModuls angesehen und es sieht so aus, als würde die gesamte Datei im Speicher gelesen. Wenn der Direktzugriff wichtiger ist als die Größenoptimierung, linecacheist dies die beste Methode.
Alok Singhal
7
mit linecache.getlin ('some_file', 4) bekomme ich die 4. Zeile, nicht die 5 ..
Juan
Unterhaltsame Tatsache: Wenn Sie im zweiten Beispiel anstelle der Liste ein Set verwenden, erhalten Sie eine Laufzeit von O (1). In einer Liste nachschlagen ist O (n). Interne Mengen werden als Hashes dargestellt, und deshalb erhalten Sie die Laufzeit O (1). In diesem Beispiel keine große Sache, aber wenn Sie eine große Liste von Zahlen verwenden und auf Effizienz achten, sind Sets der richtige Weg.
Rady
linecachescheint jetzt nur für Python-Quelldateien zu funktionieren
Paul H
Sie können auch linecache.getlines('/etc/passwd')[0:4]die erste, zweite, dritte und vierte Zeile lesen.
Zyy
30

Ein schneller und kompakter Ansatz könnte sein:

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

Dies akzeptiert alle geöffneten dateiähnlichen Objekte thefile(die dem Aufrufer überlassen bleiben, ob sie aus einer Festplattendatei oder beispielsweise über einen Socket oder einen anderen dateiähnlichen Stream geöffnet werden sollen) und eine Reihe von auf Null basierenden Zeilenindizes whatlinesund gibt a zurück Liste, mit geringem Speicherbedarf und angemessener Geschwindigkeit. Wenn die Anzahl der zurückzugebenden Zeilen sehr groß ist, bevorzugen Sie möglicherweise einen Generator:

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

Dies ist im Grunde nur zum Schleifen geeignet. Beachten Sie, dass der einzige Unterschied darin besteht return, dass in der Anweisung eher gerundete als quadratische Klammern verwendet werden , um ein Listenverständnis bzw. einen Generatorausdruck zu erhalten.

Beachten Sie außerdem, dass diese Funktionen trotz der Erwähnung von "Zeilen" und "Datei" viel, viel allgemeiner sind - sie funktionieren mit jeder iterierbaren Datei, sei es eine geöffnete Datei oder eine andere, und geben eine Liste (oder einen Generator) von Elementen zurück basierend auf ihren progressiven Artikelnummern. Also würde ich vorschlagen, angemessenere allgemeine Namen zu verwenden ;-).

Alex Martelli
quelle
@phemient, ich bin anderer Meinung - der Genexp liest sich reibungslos und perfekt.
Alex Martelli
Hervorragende und elegante Lösung, danke! In der Tat sollten auch große Dateien mit dem Generatorausdruck unterstützt werden. Kann nicht eleganter sein, oder? :)
Samuel Lampa
Gute Lösung, wie ist das im Vergleich zu der von @AdamMatan vorgeschlagenen? Die Adam-Lösung könnte schneller sein, da sie zusätzliche Informationen nutzt (Zeilennummern monoton ansteigend), die zu einem frühen Stopp führen könnten. Ich habe eine 10-GB-Datei, die ich nicht in den Speicher laden kann.
Mannaggia
2
@Mannaggia Es wird in dieser Antwort nicht genug betont, whatlinessollte aber eine sein set, da if i in whatlineses mit einem Satz schneller ausgeführt wird als mit einer (sortierten) Liste. Ich habe es zuerst nicht bemerkt und stattdessen meine eigene hässliche Lösung mit sortierter Liste entwickelt (wobei ich nicht jedes Mal eine Liste scannen musste, während ich if i in whatlinesgenau das tat ), aber der Leistungsunterschied war vernachlässigbar (mit meinen Daten) und dies Lösung ist viel eleganter.
Victor K
28

Um eine andere Lösung anzubieten:

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

Ich hoffe das geht schnell und einfach :)

KingMak
quelle
1
Hoffe, dies ist die optimale Lösung.
Maniac_user
2
Dies liest die gesamte Datei in den Speicher. Sie können auch file.read (). Split ('\ n') aufrufen und dann Array-Index-Lookups verwenden, um die interessierende Zeile zu erhalten ...
duhaime
Können Sie ein Beispiel @duhaime bieten
Anon
14

wenn Sie Zeile 7 wollen

line = open ("file.txt", "r"). readlines () [7]
MadSc13ntist
quelle
14
Ordentlich. Aber wie macht man close()die Datei beim Öffnen auf diese Weise?
Milo Wielondek
1
@ 0sh müssen wir schließen?
Ooker
1
Ja. Wir müssen danach schließen. Wenn wir eine Datei mit "with" öffnen, schließt sie sich von selbst.
Reetesh11
10

Der Vollständigkeit halber ist hier noch eine Option.

Beginnen wir mit einer Definition aus Python-Dokumenten :

Slice Ein Objekt, das normalerweise einen Teil einer Sequenz enthält. Ein Slice wird unter Verwendung der tiefgestellten Notation [] mit Doppelpunkten zwischen Zahlen erstellt, wenn mehrere angegeben sind, z. B. in Variablenname [1: 3: 5]. Die Klammer-Notation (Index) verwendet Slice-Objekte intern (oder in älteren Versionen __getslice __ () und __setslice __ ()).

Obwohl die Slice-Notation im Allgemeinen nicht direkt auf Iteratoren anwendbar ist, itertoolsenthält das Paket eine Ersetzungsfunktion:

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

Der zusätzliche Vorteil der Funktion besteht darin, dass sie den Iterator erst am Ende liest. So können Sie komplexere Dinge tun:

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

Und um die ursprüngliche Frage zu beantworten:

# how to read lines #26 and #30
In [365]: list(islice(xrange(1,100), 25, 30, 4))
Out[365]: [26, 30]
Newtover
quelle
1
Bei weitem der beste Ansatz bei der Arbeit mit großen Dateien. Mein Programm ging von 8 GB + auf fast nichts über. Der Nachteil war die CPU-Auslastung, die von ~ 15% auf ~ 40% stieg, aber die eigentliche Verarbeitung der Datei war 70% schneller. Ich werde diesen Kompromiss den ganzen Tag machen. Danke! 🎉🎉🎉
GollyJer
1
Das scheint mir am pythonischsten zu sein. Vielen Dank!
Ipetrik
10

Das Lesen von Dateien ist unglaublich schnell. Das Lesen einer 100-MB-Datei dauert weniger als 0,1 Sekunden (siehe meinen Artikel Lesen und Schreiben von Dateien mit Python ). Daher sollten Sie es vollständig lesen und dann mit den einzelnen Zeilen arbeiten.

Was die meisten hier antworten, ist nicht falsch, sondern schlechter Stil. Das Öffnen von Dateien sollte immer erfolgen, withda dadurch sichergestellt wird, dass die Datei wieder geschlossen wird.

Also solltest du es so machen:

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

Riesige Dateien

Wenn Sie zufällig eine große Datei haben und der Speicherverbrauch ein Problem darstellt, können Sie diese Zeile für Zeile verarbeiten:

with open("path/to/file.txt") as f:
    for i, line in enumerate(f):
        pass  # process line i
Martin Thoma
quelle
IMO ist es ein wirklich schlechter Stil, eine ganze Datei mit unbekannter Länge zu lesen, nur um die ersten 30 Zeilen zu erhalten. Was ist mit dem Speicherverbrauch? Und was ist mit endlosen Streams?
Rückkehr42
@ return42 Es hängt sehr stark von der Anwendung ab. Für viele ist es völlig in Ordnung anzunehmen, dass eine Textdatei eine viel geringere Größe als der verfügbare Speicher hat. Wenn Sie möglicherweise große Dateien haben, habe ich meine Antwort bearbeitet.
Martin Thoma
Vielen Dank für Ihre Hinzufügung, die das gleiche wie alok Antwort ist . Und sorry nein, ich glaube nicht, dass dies von der Anwendung abhängt. IMO ist es immer besser, nicht mehr Zeilen zu lesen, als Sie brauchen.
Rückkehr42
7

Einige davon sind sehr schön, aber es kann viel einfacher gemacht werden:

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)

Das wird einfach Listen-Slicing verwenden, es lädt die gesamte Datei, aber die meisten Systeme minimieren die Speichernutzung angemessen, es ist schneller als die meisten der oben angegebenen Methoden und funktioniert mit meinen 10G + -Datendateien. Viel Glück!

Wille
quelle
4

Sie können einen seek () -Aufruf ausführen, der Ihren Lesekopf auf ein bestimmtes Byte in der Datei positioniert. Dies hilft Ihnen nur, wenn Sie genau wissen, wie viele Bytes (Zeichen) vor der Zeile, die Sie lesen möchten, in die Datei geschrieben werden. Vielleicht ist Ihre Datei streng formatiert (jede Zeile hat eine X-Anzahl von Bytes?) Oder Sie können die Anzahl der Zeichen selbst zählen (denken Sie daran, unsichtbare Zeichen wie Zeilenumbrüche einzuschließen), wenn Sie die Geschwindigkeit wirklich steigern möchten.

Andernfalls müssen Sie jede Zeile vor der gewünschten Zeile lesen, gemäß einer der vielen hier bereits vorgeschlagenen Lösungen.

römisch
quelle
3

Wenn Ihre große Textdatei filestreng strukturiert ist (dh jede Zeile hat die gleiche Länge l), können Sie sie für die n-te Zeile verwenden

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

Haftungsausschluss Dies funktioniert nur für Dateien mit der gleichen Länge!

Michael Dorner
quelle
2

Wie wäre es damit:

>>> 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
quelle
Das ist zwar weniger effizient als das von Alok, aber meins verwendet eine with-Anweisung;)
Hamish Grubijan
2

Wenn Sie nichts dagegen haben zu importieren, macht fileinput genau das, was Sie brauchen (das heißt, Sie können die Zeilennummer der aktuellen Zeile lesen).

ennuikiller
quelle
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

quelle
Roger, mein Liebling! Dies könnte von einer with-Anweisung profitieren.
Hamish Grubijan
2

Ich bevorzuge diesen Ansatz, weil er allgemeiner ist, dh Sie können ihn für eine Datei verwenden, für das Ergebnis f.readlines()eines StringIOObjekts, was auch immer:

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
quelle
2

Hier sind meine kleinen 2 Cent für das, was es wert ist;)

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
quelle
2

Eine bessere und kleinere Änderung für Alok Singhals Antwort

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
quelle
1

@OP können Sie enumerate verwenden

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

Mit der with-Anweisung wird die Datei geöffnet, die Zeilen 26 und 30 gedruckt und die Datei geschlossen. Einfach!

user3901273
quelle
Dies ist keine gültige Antwort. nachdem der erste Aufruf readlines()des Iterators erschöpft ist und der zweite Aufruf entweder eine leere Liste zurückgibt oder einen Fehler auslöst (kann mich nicht erinnern, welcher)
Paul H
1

Sie können dies sehr einfach mit dieser Syntax tun, die bereits erwähnt wurde, aber es ist bei weitem der einfachste Weg, dies zu tun:

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

Um Zeile 3 zu drucken,

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

Ursprünglicher Autor: Frank Hofmann

verrückte_ Narzissen
quelle
1

Ziemlich schnell und auf den Punkt.

So drucken Sie bestimmte Zeilen in einer Textdatei. Erstellen Sie eine "lines2print" -Liste und drucken Sie dann einfach, wenn sich die Aufzählung "in" der lines2print-Liste befindet. Um zusätzliches '\ n' zu entfernen, verwenden Sie line.strip () oder line.strip ('\ n'). Ich mag nur "Listenverständnis" und versuche es zu verwenden, wenn ich kann. Ich mag die "with" -Methode zum Lesen von Textdateien, um zu verhindern, dass eine Datei aus irgendeinem Grund offen bleibt.

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]

oder wenn die Liste klein ist, geben Sie einfach die Liste als Liste in das Verständnis ein.

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

Gewünschte Zeile drucken. Zeile über / unter der gewünschten Zeile drucken.

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

execute ----> dline ("D: \ dummy.txt", 6) dh dline ("Dateipfad", line_number, wenn Sie möchten, dass die obere Zeile der gesuchten Zeile 1 für die untere -1 gibt, ist dies ein optionaler Standardwert genommen werden 0)

Sudhir Tataraju
quelle
0

Wenn Sie bestimmte Zeilen lesen möchten, z. B. Zeilen, die nach einer bestimmten Schwellenwertzeile beginnen, können Sie die folgenden Codes verwenden: file = open("files.txt","r") lines = file.readlines() ## convert to list of lines datas = lines[11:] ## raed the specific lines

Niharranjan Pradhan
quelle
-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
quelle
7
Das ist so unpythonisch wie es nur geht.
SilentGhost
Gibt das falsche Ergebnis, da Sie keine solchen Leseleitungen und Leseleitungen verwenden können (sie ändern jeweils die aktuelle Leseposition).
Es tut mir leid, dass ich in meinem ersten Code einen RIESIGEN Fehler übersehen habe. Der Fehler wurde behoben und der aktuelle Code sollte wie erwartet funktionieren. Vielen Dank für den Hinweis auf meinen Fehler, Roger Pate.
inspectorG4dget
-1

Ich denke das würde funktionieren

 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]
Versank
quelle
Es gab bereits ein Dutzend Readline-Methoden, als Sie dies gepostet haben - das Hinzufügen einer weiteren fügt nur Unordnung hinzu
duhaime