web-dev-qa-db-de.com

Wie springt man zu einer bestimmten Zeile in einer großen Textdatei?

Gibt es Alternativen zum folgenden Code:

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

Wenn ich eine große Textdatei (~15MB) mit Zeilen unbekannter, aber unterschiedlicher Länge bearbeite und zu einer bestimmten Zeile springen muss, deren Nummer ich im Voraus kenne? Ich fühle mich schlecht, wenn ich sie nacheinander verarbeite, wenn ich weiß, dass ich die erste Hälfte der Datei ignorieren könnte. Suchen Sie nach einer eleganteren Lösung, wenn es welche gibt.

96
user63503

linecache :

Mit dem linecache -Modul kann eine beliebige Zeile aus einer Python-Quelldatei abgerufen werden, während versucht wird, den internen Fall mithilfe eines Cache zu optimieren, bei dem viele Zeilen aus einer einzigen Datei gelesen werden. Dies wird vom Modul traceback verwendet, um Quellzeilen für die Aufnahme in das formatierte Traceback abzurufen ...

27
John Ellinwood

Sie können nicht weiter springen, ohne die Datei mindestens einmal einzulesen, da Sie nicht wissen, wo die Zeilenumbrüche sind. Sie könnten so etwas tun:

# 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])
97
Adam Rosenfield

Sie haben nicht wirklich viele Optionen, wenn die Zeilen unterschiedlich lang sind ... Sie müssen leider die Zeilenendezeichen verarbeiten, um zu wissen, wann Sie zur nächsten Zeile übergegangen sind.

Sie können dies jedoch drastisch beschleunigen UND die Speicherbelegung reduzieren, indem Sie den letzten Parameter in "open" auf einen Wert ändern, der nicht 0 ist.

0 bedeutet, dass das Lesen der Datei ungepuffert ist. 1 bedeutet, dass die Datei in Zeilen gepuffert ist, was eine Verbesserung darstellen würde. Alles über 1 (sagen wir 8k .. dh: 8096 oder höher) liest Stücke der Datei in den Speicher. Sie können immer noch über for line in open(etc): darauf zugreifen, aber Python geht nur ein bisschen weiter und verwirft jeden gepufferten Block nach seiner Verarbeitung.

19
Jarret Hardie

Ich bin wahrscheinlich durch reichlich Widder verwöhnt, aber 15 M sind nicht riesig. Mit readlines() in den Speicher lesen, mache ich normalerweise mit Dateien dieser Größe. Der Zugriff auf eine Zeile danach ist trivial.

12
SilentGhost

Da es nicht möglich ist, die Länge aller Zeilen zu bestimmen, ohne sie zu lesen, haben Sie keine andere Wahl, als alle Zeilen vor Ihrer Startlinie zu durchlaufen. Alles, was Sie tun können, ist, es schön aussehen zu lassen. Wenn die Datei wirklich sehr groß ist, möchten Sie möglicherweise einen Generator-basierten Ansatz verwenden:

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)

Hinweis: Bei diesem Ansatz basiert der Index auf Null.

4
unbeknown

Ich bin überrascht, dass niemand islice erwähnt hat

line = next(itertools.islice(Fhandle,index_of_interest,index_of_interest+1),None) # just the one line

oder wenn Sie den gesamten Rest der Datei wünschen

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

oder wenn Sie jede andere Zeile aus der Datei haben möchten

rest_of_file = itertools.islice(Fhandle,index_of_interest,None,2)
for odd_line in rest_of_file:
    print odd_line
3
Joran Beasley

Wenn Sie die Position in der Datei (und nicht die Zeilennummer) vorher kennen, können Sie file.seek () verwenden, um zu dieser Position zu gelangen.

Edit: Sie können die Funktion linecache.getline (Dateiname, Leineno) verwenden, die den Inhalt der Zeile Leineno zurückgibt, jedoch erst, nachdem die gesamte Datei in den Speicher eingelesen wurde. Gut, wenn Sie zufällig auf Zeilen innerhalb der Datei zugreifen (wie Python selbst möglicherweise ein Traceback drucken möchte), aber nicht gut für eine 15-MB-Datei.

2
Noah

Was erzeugt die Datei, die Sie bearbeiten möchten? Wenn Sie sich unter Ihrer Kontrolle befinden, können Sie zum Zeitpunkt, zu dem die Datei angehängt wird, einen Index erstellen (welche Zeile befindet sich an welcher Position). Die Indexdatei kann eine feste Zeilengröße haben (Leerzeichen oder 0 aufgefüllte Zahlen) und ist auf jeden Fall kleiner. Und kann somit schnell gelesen und verarbeitet werden. 

  • Welche Linie willst du? 
  • Berechnet den Byte-Versatz der entsprechenden Zeilennummer in der Indexdatei (möglich, da die Zeilengröße der Indexdatei konstant ist).
  • Verwenden Sie Suchen oder was auch immer, um direkt zu springen, um die Zeile aus der Indexdatei zu erhalten.
  • Parse, um den Byte-Offset für die entsprechende Zeile der aktuellen Datei zu erhalten.
2
kamathln

Wenn Sie nicht die gesamte Datei im Speicher lesen möchten, müssen Sie möglicherweise ein anderes Format als Nur-Text verwenden.

natürlich hängt alles davon ab, was Sie versuchen und wie oft Sie durch die Datei springen.

Wenn Sie beispielsweise in derselben Datei zu Zeilen viele Male springen, und Sie wissen, dass sich die Datei während der Arbeit nicht ändert, können Sie Folgendes tun:
Durchlaufen Sie zunächst die gesamte Datei und notieren Sie den "Suchort" einiger Schlüsselzeilennummern (wie z. B. je 1000 Zeilen).
Wenn Sie die Zeile 12005 wünschen, springen Sie zur Position 12000 (die Sie aufgezeichnet haben), und lesen Sie dann 5 Zeilen, und Sie wissen, dass Sie sich in Zeile 12005 befinden

2
hasen

Ich hatte das gleiche Problem (muss aus einer großen Datei-spezifischen Zeile abrufen).

Natürlich kann ich jedes Mal alle Datensätze in der Datei durchlaufen und stoppen, wenn der Zähler der Ziellinie entspricht. Dies funktioniert jedoch nicht effektiv, wenn Sie mehrere bestimmte Zeilen erhalten möchten. Dies führte dazu, dass das Hauptproblem gelöst wurde - wie direkt zum erforderlichen Ort der Datei verfahren wird.

Die nächste Entscheidung habe ich herausgefunden: Zuerst habe ich das Wörterbuch mit der Startposition jeder Zeile abgeschlossen (Schlüssel ist Zeilennummer und Wert - kumulierte Länge der vorherigen Zeilen).

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

zielfunktion:

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

t.seek (line_number) - Befehl, der die Bereinigung der Datei bis zum Beginn der Zeile ausführt. Wenn Sie also das nächste Mal readline festlegen, erhalten Sie Ihre Ziellinie.

Mit diesem Ansatz habe ich viel Zeit gespart. 

2
user3810114

Enthalten die Zeilen selbst Indexinformationen? Wenn der Inhalt jeder Zeile etwa "<line index>:Data" war, könnte der seek()-Ansatz für eine binäre Suche in der Datei verwendet werden, auch wenn der Betrag von Data variabel ist. Sie suchen zum Mittelpunkt der Datei, lesen eine Zeile, prüfen, ob der Index höher oder niedriger als der gewünschte Index ist usw.

Ansonsten ist das Beste, was Sie tun können, nur readlines(). Wenn Sie nicht alle 15 MB lesen möchten, können Sie das sizehint-Argument verwenden, um zumindest einige readline()s durch eine geringere Anzahl von Aufrufen an readlines() zu ersetzen.

1
DNS

Wenn Sie sich mit einer Textdatei & - basierend auf Linux-System beschäftigen, können Sie die Linux-Befehle verwenden. 
Für mich hat das gut funktioniert!

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)
0
HongKun Yoo

Sie können mmap verwenden, um den Versatz der Linien zu ermitteln. MMap scheint der schnellste Weg zu sein, eine Datei zu verarbeiten

beispiel:

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

verwenden Sie dann f.seek (Offsets), um zur gewünschten Zeile zu gelangen

0
george

Hier ist ein Beispiel, bei dem 'readlines (sizehint)' verwendet wird, um ein Stück Zeilen gleichzeitig zu lesen. DNS wies auf diese Lösung hin. Ich habe dieses Beispiel geschrieben, weil die anderen Beispiele hier einzeilig sind.

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)
0
Andrew Dalke