Bestimmte Zeile (Zeilennummer) in Textdatei lesen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Hallo
Gibt es das nicht direkt, oder finde ich es nur nicht?

Habe Textdateien mit je 4'000'000 Zeilen.
Diese will ich öffnen und z.B. genau nur die Zeile Nr. 1000 lesen, um diese weiter zu verarbeiten.
sowas in der Art unten. Liefert aber nicht die Zeile 1000. sondern die ersten 1000 Zeichen der ersten Zeile.

Ursprungsidee war mit csv zu arbeiten, das einlesen dauert bei der grösse aber über 12 sekunden. (passiert zyklisch - darum zu lange)
Eine Textdatei ist in 0,nichts geladen.

Code: Alles auswählen

datei = open('test.txt','r')
zeile = datei.readline(1000)
print(zeile)    
datei.close()
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

Das gibt es schon: itertools.islice. Trotzdem müssen dafür die ersten 1000 Zeilen gelesen werden. Bei solch einer Größenordnung solltest Du Dich nach einer anderen Art der Speicherung umschauen.
Was ist denn die genaue Anwendung?
Benutzeravatar
DeaD_EyE
User
Beiträge: 1232
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Einfacher wäre es, wenn jede Zeile die gleiche Länge hätte. Dann wäre die Berechnung eines offsets recht einfach: ZeilenNr * ZeilenLänge
Vermutlich wirst du unterschiedlich lange Zeilen vorliegen haben.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Wenn ich die Zeile 3'999'000 brauche, muss ich ja auch "lange" lesen.
Mache gerade folgendes, lese ganze Datei Zeilenweise in eine Liste. Zeit ca. 3.5 Sek. Damit könnte ich leben. Das Datei einlesen ist nicht so häufig, dafür aber brauche ich jede Sekunde einen Wert aus dieser Datei.

Wozu ich das brauche?
Es sind Text (csv) - Dateien mit 3D Koordinaten (X,Y,Z).
X und Y sind schön in einem definierten fixen Raster, aufgrund dessen weiss ich, in welcher Zeile sich meine gesuchte Z-Koordinate passend zu X,Y befindet.
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

Verstehe ich nicht. Wenn Du die ganze Datei in einer Liste hast, dann ist doch das Lesen eines bestimmten Wertes kein Problem.
Lesen macht man mit numpy und nicht per readline.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1232
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

kussji hat geschrieben: Dienstag 29. September 2020, 13:34 Wenn ich die Zeile 3'999'000 brauche, muss ich ja auch "lange" lesen.
Wenn du vorher den offset weißt (Zeilen haben identische Länge), kannst du mit fd.seek(offset) dort hinspringen und dann fd.read(zeilenlänge).
Aber wie gesagt, geht nur bei gleich langen Zeilen.


Ansonsten -> bei jeder Änderung Datei neu einlesen. Das ist, was so lange dauert.
Sobald der Inhalt im Speicher ist, geht die Suche sehr schnell.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Sirius3 hat geschrieben: Dienstag 29. September 2020, 13:48 Verstehe ich nicht.
Sorry, war wohl nicht klar gesagt. Ja das einlesen der Datei in die Liste und anschliessend mit der Liste arbeiten geht ohne Probleme.

Also es funktioniert - aber wenn jemand für das "Problem" eine bessere - effizientere Lösung hat - gerne.

Danke euch
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

DeaD_EyE hat geschrieben: Dienstag 29. September 2020, 14:00 Wenn du vorher den offset weißt (Zeilen haben identische Länge), kannst du mit fd.seek(offset) dort hinspringen und dann fd.read(zeilenlänge).
Aber wie gesagt, geht nur bei gleich langen Zeilen.
@DeaD_EyE: Vielen Dank für den Hinweis, ich glaube damit sollte es gehen, habe mal kurz getestet, sieht nicht schlecht aus. Ich habe Glück und die Zeilen sind immer gleich lang :-).
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Sirius3 hat geschrieben: Dienstag 29. September 2020, 13:48 Lesen macht man mit numpy und nicht per readline.
Wie hast Du das gemeint mit numpy, arbeite selten damit?
So?

Code: Alles auswählen

y = np.loadtxt('test.txt')
Falls ja - das dauert bei meiner grossen Datei eine Ewigkeit (86 Sekunden)
Die Textdatei in eine Liste zu lesen dauert etwa 4-5 Sekunden.
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

Das kann ich kaum glauben. Kannst Du mal den Code zum Lesen zeigen?
Benutzeravatar
DeaD_EyE
User
Beiträge: 1232
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Du kannst testen wie lang jede Zeile ist:

Code: Alles auswählen

def line_lengths(file):
    with open(file, "rb") as fd:
        return {len(line) for line in fd if line.strip()}
Die letzte Zeile ist meistens nur ein Newline.
Wenn du mehr als eine Zahl bekommst, kann das ggf. noch der Header sein.
Wenn du viele Ergebnisse bekommst, kannst du das mit dem Offset so nicht machen.

Wie oft ändert sich die Datei?
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
heyJo
User
Beiträge: 25
Registriert: Mittwoch 24. Januar 2018, 20:49
Wohnort: Köln

Hallo zusammen,

ich fand die Fragestellung auch für mich interessant. Deshalb habe ich mal folgende probiert:
- Eine Datei mit 4.000.000 Zeilen erzeugt
- Mittels zwei verschiedener Verfahren eine x-beliebige Zeile rausgefischt

Ich habe die Zeiten nicht gestoppt, aber „gefühlt“ habe ich keine Unterschiede bei der Auswahl festgestellt. Die Schiene numpy oder panadas habe ich nicht getestet.

Gruß
Jo

Code: Alles auswählen

with open ("Koordinaten.txt","w") as outputfile:
    for i in range (0,3999999):
        print("70168.862;50300.787;40.000",file=outputfile)

Code: Alles auswählen

with open ("Koordinaten.txt","r") as file:
    n=0
    for line in file:
        n +=1
        if n == 3999998:
            print(line,end="")

Code: Alles auswählen

from itertools import islice

with open ("Koordinaten.txt","r") as file:
    print (list(islice(file,3999997,3999998,)))
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@heyJo: die beiden Alternativen machen ja auch fast das selbe, was die ausgeführten Befehle betrifft, wobei die erste Variante mit enumerate gemacht werden sollte:

Code: Alles auswählen

for n, line in enumerate(line):
    if n == 3999998:
        print(line, end="")
@kussji: ja, numpy ist nicht gerade schnell beim Lesen, einer der schnellsten Leser ist pandas.readcsv.
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Hallo zusammen
Danke für die neuen Inputs, werde diese morgen mal zu Gemüte führen.

@Sirius: Erster Code mit numpy braucht sehr lange zum Lesen. Zweiter Code mit "liste" benötigt _nur_ 2.2 Sekunden!

@DeaD_EyE: Mir ist noch in den Sinn gekommen, dass andere Dateien die vielleicht später kommen nicht mehr die selben Zeilenlängen haben, weswegen ich wieder zu "alles" einlesen tendiere (Kompatibilität).
Die Dateien selber ändern nie, was ändert ist welche Datei ich lesen muss. Im Moment sind es 30 Dateien, wobei in der Regel meist nur 3-4 aktiv genutzt werden, die anderen liegen brach und werden logischerweise nicht geladen.
Zeitlich ist Dein Lösungsvorschlag natürlich der Hammer wenige ms.

Eine Dateizeile sieht z.B. so aus (X,Y,Z): 2624000.25 1133999.75 1121.99. Das X/Y Raster ist konstat mit 0.25;

Code: Alles auswählen

from time import time
import numpy as np
t1 = time()
y = np.loadtxt('test.txt')
t2 = time()
print('Zeit: ',t2-t1)
print(y[0])

Code: Alles auswählen

#Output:
Zeit:  102.8839020729065
[2.62400025e+06 1.13399975e+06 1.12199000e+03]

Code: Alles auswählen

t1 = time()
datei = open('test.txt','r')
liste = []
for zeile in datei:
    liste.append(zeile.rstrip('\n\r'))
t2 = time()
print('Zeit: ',t2-t1)
print(liste[3999999])
datei.close()
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich kann kaum glauben, dass das einzige was Du von den Dateien brauchst, eine einzige Zeile ist.
Wenn es Dir um Geschwindigkeit geht, nimm pandas.read_csv.
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Sirius3 hat geschrieben: Mittwoch 30. September 2020, 19:47 Ich kann kaum glauben, dass das einzige was Du von den Dateien brauchst, eine einzige Zeile ist.
Wenn es Dir um Geschwindigkeit geht, nimm pandas.read_csv.
Okey versuche es morgen mal mit pandas. Natürlich brauche ich nicht nur eine einzige Zeile, sondern wie ich schon sagte jede Sekunde eine Zeile meist eine andere aus der selben Datei. Dabei kann es vorkommen, dass ich eine andere Datei brauche, welche ich dann bei Bedarf einlese um mit dieser zu arbeiten. Alle 30 Dateien bei Start einlesen halte ich etwas für unsinnig, da ich ja meist nur 3-4 brauche. Dies meine Überlegung wegen Speicherresourcen oder wäre das Nullproblemo? Habe mich mit Speichergrösse, -platz nie befasst. (eine Datei hat 118MB) Die werden in den Arbeitspeicher gelanden - oder? Und jenachdem wieviel Arbeitsspeicher ich halt habe, klappt es oder nicht?
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

drei bis vier Dateien im Speicher zu halten, ist heutzutage kein Problem mehr. Wenn die x und y-Werte so regelmäßig sind, dann braucht man die gar nicht. Damit brauchen alle 30 Dateien nicht mal ein Gigabyte.
Zudem speichert man die Daten binär, dann ist es völlig egal ob die Dateien als Memory-mapped Files in den Speicher passen, oder nicht.
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Die Dateien in der Grösse selbst zu optimieren habe ich auch schon gedacht.
Ich bekomme diese von extern und habe keinen Einfluss darauf wie sie erstellt werden. Darum wollte ich sie im ersten Schritt mal unverändert lassen.
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

Kannst du mal deine Aufgabe konkret beschreiben? Warum brauchst du von den vielen Dateien nur wenige und warum von den wenigen jede Sekunde nur einen Wert? Wer gibt dir vor, welchen Wert du brauchst?
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Zu detailliert möchte ich das nicht beschreiben. Die "Suchwerte" jede Sekunde liefert ein GNSS, in den Dateien stehen Topodaten.
...und fragt mich jetzt nicht, warum ich das nicht näher beschreiben will :-)
Antworten