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.
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 Modultraceback
verwendet, um Quellzeilen für die Aufnahme in das formatierte Traceback abzurufen ...
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])
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.
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.
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.
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
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.
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.
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
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.
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.
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)
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
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)