seek() in Python3

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.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich würde behaupten auch die Version von Sirius3 ist noch nicht korrekt weil ``for line in file:`` nicht zeilenweise lesen muss und das AFAIK auch tatsächlich nicht tut. Das *liefert* zeilenweise, liest aber in grösseren Blöcken und puffert, aus Effizienzgründen. Wenn man tatsächlich garantiert Zeile für Zeile lesen will, muss man explizit `readline()` für jede Zeile benutzen. Zum Beispiel als ``for line in iter(file.readline, b""):``.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@__blackjack__: ob irgendwo intern gepuffert wird, ist doch für den Anwender egal. `open` liefert immer einen BufferedReader, `readline` verhält sich also genau gleich wie der Iterator.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sirius3: Dann ist das neu in Python 3. In Python 2 konnte man iterieren und `readline()` nicht mischen weil iterieren gepuffert hat und `readline()` die C-Funktion auf der Datei direkt verwendet hat.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

So ganz egal ist internes puffern nicht, den dass führt ja dazu dass es Unterschied gibt zwischen deiner Position aus Perspektive des Betriebssystems und deiner Anwendung. Das muss ja irgendwie in Einklang gebracht werden.

Wenn man die Datei übrigens in "text mode" öffnet bekommt man etwas zurück dass TextIOBase implementiert. .tell() und .seek() auf TextIOBase arbeiten nicht in bytes: https://docs.python.org/3/library/io.ht ... OBase.seek. Ich denke mal damit wird vermieden dass man invalide Strings durch ein seek an die falsche Stelle produziert.
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@DasIch: es ist in soweit egal, weil ich als Anwender die Dateipositionen des Betriebssystems gar nicht zu Gesicht bekomme.
Und doch, tell und seek arbeiten auch bei Textdateien auf Byteniveau, weshalb es ja zu Problemen kommen kann, wenn man das mischt.
offset must either be a number returned by TextIOBase.tell(), or zero. Any other offset value produces undefined behaviour.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Also tell() sollte schon das liefern, was auf "seiner" Abstraktionsebene als aktuelle Position betrachtet wird. Irgendwelche internen Puffer-Positionen hätten da wenig Sinn für den Anwender. 🤷‍♂️

Und was ein Zeichen ist, hängt ja von der Konfiguration des Wrappers ab. Dementsprechend sind das auf unterster Ebene einzelne Bytes, können auf höheren Ebenen aber durchaus Codepoints, d.h. auch mehrere Bytes sein. Wenn man beim zum tell() passenden seek() bleibt, dann spielt das aber ohnehin keine Rolle.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Sirius3 hat geschrieben: Montag 5. Juli 2021, 14:39 Und doch, tell und seek arbeiten auch bei Textdateien auf Byteniveau, weshalb es ja zu Problemen kommen kann, wenn man das mischt.
offset must either be a number returned by TextIOBase.tell(), or zero. Any other offset value produces undefined behaviour.
Das sagt ja nur dass eine Nummer erwartet die von .tell() kommt, nicht dass die Number sich auf bytes bezieht. Liest man ein bisschen weiter um zu sehen was .tell() dazu sagt trifft man dann auf:
Return the current stream position as an opaque number. The number does not usually represent a number of bytes in the underlying binary storage.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Nachdem ich nun den Code für seek() und tell() jeweils für den Text- und Binärmodus gesehen habe, würde ich das nur im Binärmodus empfehlen, wie ja schon gezeigt wurde. Im Text-Modus ist das alles sehr kompliziert programmiert, kann in irgendwelchen "Illegal States" landen und wirkt insgesamt jetzt nicht sooo vertrauenserweckend auf mich (nichts für ungut), wobei ich da eh irgendwann aufgegeben habe. f.tell() während man zeilenweise iteriert, wirft sowieso einen OSError im Textmodus, während es im Binärmodus ("rb") klappt wie erwartet. Die Positionsangaben entsprechen im Binärmodus tatsächlich den Bytes, da auf einem Char-Pointer herumgesprungen wird. Keine Ahnung, warum die Doku da so vage ist. Vielleicht wollte man es als Implementierungsdetail belassen und daher nicht näher festlegen.

EDIT: Das soll jetzt nicht heißen, dass tell() im Textmodus aus meiner Sicht grundsätzlich kaputt wäre. Aber in Abhängigkeit von Encoding und Verwendung wären mir das zu viele Unsicherheiten. Soll aber natürlich jeder selbst entscheiden.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

DerSuchende hat geschrieben: Montag 5. Juli 2021, 04:58 Wenn auch meine Dateien komplett in den Arbeitsspeicher passen würden, so wollte ich
nicht mit einer Schaufel Erde nach einer Fliege werfen, nur weil genügend Erde vorrätig
ist.
Premature optimization. Du denkst über Dinge nach, die Dein Betriebssystem viel besser kann als Du. Und schießt Dir dabei, Pardon: in Deine eigenen Füße. ;-)
DerSuchende
User
Beiträge: 29
Registriert: Montag 21. Februar 2011, 07:37

Premature optimization. Du denkst über Dinge nach, die Dein Betriebssystem viel besser kann als Du. Und schießt Dir dabei, Pardon: in Deine eigenen Füße. ;-)
Nun, da die hier erarbeitete Lösung ja im Prinzip offensichtlich funktioniert, ist der Schuss wohl an den Füßen vorbeigegangen. Es ging ja nur darum, einen etwas intelligenteren Zugriff auszuprobieren. Und wenn es zuletzt nur ein Gedankenspiel bleibt, so ist doch Python dafür genau die richtige Spielwiese :-)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Naja, so gut sind Betriebssysteme beim Swapping auch nicht, erst recht nicht wenn man das Zugriffspattern nicht kommuniziert. Letztendlich passen Daten aber in der Praxis nahezu immer in den Arbeitsspeicher, die Frage ist nur ob du dir es auch leisten kannst.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@DerSuchende: Ist jetzt eigentlich diese Lösung dein Favorit? Dir ist schon klar, dass hier immer noch alle Zeilen durchlaufen werden, um die "Breakpoints" zu ermitteln? Die ermittelte Struktur samt Zugriff ist ja nur hilfreich bei langlebigen Programmen und nicht, wenn der gesamte Ablauf immer wieder neu angestoßen wird. Welches von beiden wäre denn dein Szenario? Ist vielleicht nth() bzw nth_or_last() aus dem (externen) more_itertools-Modul schon ausreichend für deine Zwecke? Das Iterable wäre dabei halt dein Stream, der die einzelnen Zeilen liefert. Dann benötigt man kein tell() und seek() für die Wahl einer einzelnen Zeile.
DerSuchende
User
Beiträge: 29
Registriert: Montag 21. Februar 2011, 07:37

@DasIch: Beim Arbeitsspeicherverbrauch wollte ich sparsam vorgehen, damit eine Lösungsidee leicht angepasst eventuell auch unter MikroPython einsetzbar bleibt. Allerdings bin ich mir nicht sicher, ob das Python nicht trotzdem hintereinander n Datensätze hereinholt, wenn ich den Datensatz n+1 adressiere. Das muss ich nochmal hinterleuchten. Unter dem Blickwinkel ist es vielleicht doch besser, wenn ich, wie üblich, die Zeilen nacheinander überschreibend einlese, bis Zeile n endlich kommt.
DerSuchende
User
Beiträge: 29
Registriert: Montag 21. Februar 2011, 07:37

@snafu: Also so richtig glücklich bin ich mit dieser Vorgehensweise nicht, denn ich bin mir nicht sicher, ob alle Schwierigkeiten beseitigt sind.
Aber da durch das Aufzeichnen der Positionen des Lesezeigers immer auf das Ende einer Zeile gezeigt wird, was ja das einbytige '\n' ist, wird niemals danebengegriffen. Und die dann treffsicher herausgelesene Zeile ist ja dann ja ein String (weil wir ja von einer Textdatei reden) und aus dem kann ich ja durch string[index] jede beliebige Position sauber ansteuern. Aber wenn ich mir überlege wie einfach es ist, mit readlines alles in einer einzigen Liste vorzufinden, mit der man bequem arbeiten kann, überlege ich es mir noch 3x, ob ich mir die Ursprungsidee wirklich antun muss. Aber es war eben so ein verrückter Gedanke aus meiner Vor-Python-Zeit, wo man selbstgeschrienbene Routinen auf Direktzugriffsdateien losgelassen hat, die im dBase-Format organisiert waren. :-)
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

DerSuchende hat geschrieben: Montag 5. Juli 2021, 04:58 Wenn auch meine Dateien komplett in den Arbeitsspeicher passen würden, so wollte ich
nicht mit einer Schaufel Erde nach einer Fliege werfen, nur weil genügend Erde vorrätig
ist.
Stattdessen einen Bagger zu bauen, um dann mit dessen Schaufel nach der Fiege zu schlagen, ist jedenfalls... kreativ. ;-)
DerSuchende
User
Beiträge: 29
Registriert: Montag 21. Februar 2011, 07:37

@LukeNukem: Ok, ich sehe es ein, aber es hat zumindest Erde gespart!
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@DerSuchende: Manchmal ist es gar nicht verkehrt so einzusteigen wie Du, oder zu versuchen das Rad neu zu erfinden, um zu verstehen, wie die Dinge funktionieren. Die Erkenntnis, dass tell und seek im Textmodus keine so gute Idee sind und es besser ist gegen Daten im RAM zu arbeiten, so sie denn hineinpassen, gehört halt dazu. Spannend wird es, wenn die Daten nicht mehr ins RAM passen. Aber auch dafür gibt es bereits fertige Lösungen, die einem das Leben leichter machen können.
Und mit der Erde sollte man immer pfleglich umgehen :)
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Nur mal so am Rande: es gibt auch Python-Module um DBF-Dateien zu verarbeiten. Das Format ist nicht tot zu kriegen und wird unter anderem im GIS-Bereich noch aktiv verwendet.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten