Kann es sein, dass seek() in den Varianten seek(relativ, 1) und seek(anstand_vom_ende, 2) nur in Python2 aber nicht in Python3 funktionieren?
Oder hat sich die Syntax geändert?
seek() in Python3
Nicht laut Dokumentation https://docs.python.org/3/library/os.html#os.lseek - und es waere auch ungewoehnlich, weil das nur eine duenne Schicht ueber dem eigentlichen Bestriebssystemsaufruf ist. Der Fehler wird eher woanders liegen, wenn du da gerade ein Programm von 2 auf 3 portierst.
-
- User
- Beiträge: 29
- Registriert: Montag 21. Februar 2011, 07:37
Also ich hatte es schon auf 7-Zeiler reduziert und es ging mit Py2 tadellos und Py3 brachte die Fehlermeldung
io.UnsupportedOperation: can't do nonzero end-relative seeks
Habe im Netz aber folgenden Wrapper gefunden:
Ich arbeite übrigens unter Linux, aber glaube nicht, dass es daran liegt.
io.UnsupportedOperation: can't do nonzero end-relative seeks
Habe im Netz aber folgenden Wrapper gefunden:
Code: Alles auswählen
import io
def textio_seek(fobj, amount, whence=0):
fobj.buffer.seek(amount, whence)
return io.TextIOWrapper(
fobj.buffer, encoding=fobj.encoding, errors=fobj.errors,
newline=fobj.newlines)
# und dann weiter:
f = textio_seek(f, 7, 2)
Ich arbeite übrigens unter Linux, aber glaube nicht, dass es daran liegt.
- __blackjack__
- User
- Beiträge: 13116
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@DerSuchende: Du kannst bei Textdateien halt nicht unabhängig von der Kodierung und den Zeilenenden sagen was das eigentlich bedeutet 7 (ja was eigentlich) vom Ende der Datei zurück zu gehen. Darum geht das nicht mehr. Der Wrapper da kann funktionieren, muss aber halt auch nicht.
Was willst Du da eigentlich machen?
Was willst Du da eigentlich machen?
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
-
- User
- Beiträge: 29
- Registriert: Montag 21. Februar 2011, 07:37
Ich hatte schon so ein ungutes Gefühl, dass es mit der Codierung zusammenhängt. Ich wollte eine Textdatei einlesen, dabei inidizieren, d.h. zwei Listen anlegen, die die jeweiligen Zeilenlängen und die kumulative Zeichenmenge an der Position x erfassen, so dass ich aus der reinen Textdatei eine Art Direktzugriffsdatei machen kann, auch wenn die Zeilen unterschiedliche Länge haben. Dann kann ich eben auf Zeile x sofort zugreifen und mir das Zeichen y herausholen und ggf. auch ändern. Und das, ohne die komplette Datei im Speicher zu halten oder für jeden Zugriff alle Zeilen von Anfang an zu durchmustern.
Aber aus der Fehlermeldung entnehme ich, dass der 2. Parameter von seek() mit 1 oder 2 nur akzeptiert wird, wenn der erste Parameter 0 ist.
Notfalls muss ich es eben mit Python 2 machen. Oder ich mache eben eine kleine Nebenrechnung und ermittle so die gesuchte Position.
Das direkte Setzen des Zeigers funktioniert ja. Das mit den relativen Zeigern ist ja nur ein Schmackerl, was notfalls verzichtbar ist.
Aber aus der Fehlermeldung entnehme ich, dass der 2. Parameter von seek() mit 1 oder 2 nur akzeptiert wird, wenn der erste Parameter 0 ist.
Notfalls muss ich es eben mit Python 2 machen. Oder ich mache eben eine kleine Nebenrechnung und ermittle so die gesuchte Position.
Das direkte Setzen des Zeigers funktioniert ja. Das mit den relativen Zeigern ist ja nur ein Schmackerl, was notfalls verzichtbar ist.
-
- User
- Beiträge: 29
- Registriert: Montag 21. Februar 2011, 07:37
Stimmt, ihr habt recht. Habe mir die Datei nochmal im Hexeditor angeguckt. Klar, woher soll das arme Script wissen, wieviele Byte meine unterschiedlichsten Zeichen haben. Da ich meine Anwendung glücklicherweise nicht für Büroarbeiten einsetzen will, sondern für technische Steuerungen obliegt es mir, nur 1-Byte-Zeichen anzuwenden und dann geht es ja. Und dann ändert sich beim editieren einzelner Zeichen auch nicht die Dateilänge. Ich hatte für kurze Zeit zu sorglos drauflos programmiert bis ich stolperte. Unser schönes Python hat nämlich keine wirklichen Grenzen, man muss eben nur mitdenken
Aber vielleicht sind feste Zeilenlängen dabei auch noch der einfachere Weg.
Aber vielleicht sind feste Zeilenlängen dabei auch noch der einfachere Weg.
-
- User
- Beiträge: 29
- Registriert: Montag 21. Februar 2011, 07:37
also es handelt sich um eine Art Log-Dateien, wobei jede Zeile für einen Tag steht. Die Anzahl der Einträge können von Tag zu Tag variieren. Ich wollte lesend nur gezielt auf ein Datum springen können, ohne alle Zeilen davor durchlaufen zu müssen. Die Menge der Daten ist noch unklar. Aber es passt natürlich einiges in den RAM hinein. Ihr denkt wohl mehr an ein Directory mit Datum als Schlüssel und das ganze gepickelt?
Das Ändern von Dateneinträgen ist nicht wichtig, es kommt auf den schnellen Zugriff an. Aber die Daten entstehen eben Log-Datei-typisch seriell. Deshalb war ich für die Rohdaten von einer Textdatei ausgegangen. Aber ich werde nochmal in Ruhe drüber nachdenken. Efffektive Datenstrukturen ersparen später viel Programmierarbeit.
Das Ändern von Dateneinträgen ist nicht wichtig, es kommt auf den schnellen Zugriff an. Aber die Daten entstehen eben Log-Datei-typisch seriell. Deshalb war ich für die Rohdaten von einer Textdatei ausgegangen. Aber ich werde nochmal in Ruhe drüber nachdenken. Efffektive Datenstrukturen ersparen später viel Programmierarbeit.
Ich glaube, du machst dir zu viele Gedanken um Probleme, die noch gar nicht aufgetreten sind.
Und inzwischen bist du sehr weit vom Start des Themas entfernt. Denn dort wolltest du ja noch einzelne Bytes ersetzen - jetzt müssen die Daten gar nicht geändert werden?!
Wenn du wirklich wahlfreien Zugriff auf einzelne Datensätze brauchst - und das möglichst fix - macht auch pickle keinen Sinn.
Je nach Art, was dort gelockt wird, würde ich da mal sqlite Vorschlagen.
Und inzwischen bist du sehr weit vom Start des Themas entfernt. Denn dort wolltest du ja noch einzelne Bytes ersetzen - jetzt müssen die Daten gar nicht geändert werden?!
Wenn du wirklich wahlfreien Zugriff auf einzelne Datensätze brauchst - und das möglichst fix - macht auch pickle keinen Sinn.
Je nach Art, was dort gelockt wird, würde ich da mal sqlite Vorschlagen.
Du könntest dir auch die Dateilänge merken. Wenn diese sich durch einen neuen Log-Eintrag verändert, liest du die neuen Daten ab der alten Länge.
Wobei Python sehr effizient ist beim Verarbeiten von Text-Streams. Bist du sicher, dass es soviel ausmacht, wenn die Zeilen wiederholt komplett durchlaufen werden? seek() ist halt etwas tricky, weil man sich darauf verlassen muss, dass es auf jedem System korrekt bzw wie erwartet funktioniert.
Wobei Python sehr effizient ist beim Verarbeiten von Text-Streams. Bist du sicher, dass es soviel ausmacht, wenn die Zeilen wiederholt komplett durchlaufen werden? seek() ist halt etwas tricky, weil man sich darauf verlassen muss, dass es auf jedem System korrekt bzw wie erwartet funktioniert.
Man könnte die Daten in eine Art von Datenbank stecken. Speziell für Logdaten gibt es da tolle Lösungen, von der PostgreSQL-Erweiterung Timescaledb über eine Suchmaschine mit Zeitreihenfunktion wie Elasticsearch, bis hin zu einer In-Memory-Lösung wie Redis. Okay, Elasticsearch ist für diesen Anwendungsfall vielleicht etwas überdimensioniert... All diese Dinge sind sehr einfach aufzusetzen und zu betreiben, zumal Redis und PostgreSQL in so ziemlich allen, und Timescaledb in vielen Distributionen bereits in den Repositories der Distributoren enthalten sind.DerSuchende hat geschrieben: ↑Sonntag 4. Juli 2021, 20:19 also es handelt sich um eine Art Log-Dateien, wobei jede Zeile für einen Tag steht. Die Anzahl der Einträge können von Tag zu Tag variieren. Ich wollte lesend nur gezielt auf ein Datum springen können, ohne alle Zeilen davor durchlaufen zu müssen. Die Menge der Daten ist noch unklar. Aber es passt natürlich einiges in den RAM hinein. Ihr denkt wohl mehr an ein Directory mit Datum als Schlüssel und das ganze gepickelt?
Das Ändern von Dateneinträgen ist nicht wichtig, es kommt auf den schnellen Zugriff an. Aber die Daten entstehen eben Log-Datei-typisch seriell. Deshalb war ich für die Rohdaten von einer Textdatei ausgegangen. Aber ich werde nochmal in Ruhe drüber nachdenken. Efffektive Datenstrukturen ersparen später viel Programmierarbeit.
Nebenbei könntest Du Deine Datei auch einfach mit mmap in den Speicher mappen, aber ehrlich gesagt... Ein Eintrag pro Tag, das wären popelige 3600 Einträge in zehn Jahren, ich bitte Dich -- das ist doch gar nichts, wenn da nicht jeder Eintrag extrem viele Datenpunkte hat. Insofern befürchte ich, daß Deine Überlegungen hinsichtlich Performance und "nicht alle Daten in den Speicher laden" erstens ein Fall von "premature optimization" sind, und mit großer Wahrscheinlichkeit ohnehin fehlgehen -- denn moderne Betriebssysteme wie Linux haben da so etwas, das nennt sich Dateisystempuffer, das findest Du in der Ausgabe von top(1) und free(1) unter "buff/cache" (auf deutsch: "Puff/Cache" in top(1) und "Zwischen" in free(1)). Da wird die Datei beim Öffnen ohnehin in den Arbeitsspeicher, mithin: den Dateisystempuffer geladen, völlig unabhängig davon, ob Du die Datei dann einfach seriell liest oder darin mit seek darin herumspringst. Von daher bitte Donald E. Knuth nicht vergessen: "premature optimization is the root of all evil".
-
- User
- Beiträge: 29
- Registriert: Montag 21. Februar 2011, 07:37
Erst einmal vielen Dank für die vielen wertvollen Überlegungen. Wenn ich auf die ursprüngliche Idee verzichte, in der Datei byteweise herumschreiben zu wollen, funktioniert es jetzt doch, da ich mich lesend immer an den Wagenrückläufen entlanghangele. Hier mal mein Versuch, um meine eigentliche Absicht zu demonstrieren:
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. Es war eigentlich nur ein neugieriger Versuch, ob das so auch gehen würde.
Also nochmals vielen Dank, die Denkanstöße haben mir trotzdem geholfen, manche schon
im Vorgriff auf kommende Projekte.
Code: Alles auswählen
# Seriell2Direktzugriff
# Testfile erzeugen
f = open('text', 'w')
f.write('Hallo\n')
f.write('Du wohnst\n')
f.write('zu Hause\n')
f.write('Und Du bist\n')
f.write('hier der Chef!\n')
f.write('ÄÖÜäöüß[]{}€#><*\n')
f.close()
posindex = [0,]
zuletzt = 0
# Index für Testfile aufbauen
f = open('text', 'r')
while True:
zeile = f.readline()
if zeile == '': break
zuletzt += len(zeile)
posindex.append(zuletzt)
# print(zeile[:-1])
f.close()
# Zeigen der Indexliste
print(posindex)
# Heraussuchen einer vorgebenen Zeile
suchpos = 5
f = open('text', 'r')
f.seek(posindex[suchpos])
ergebnis = f.read(posindex[suchpos+1] - posindex[suchpos])
print(f.tell())
print('['+ergebnis[:-1]+']')
f.close()
nicht mit einer Schaufel Erde nach einer Fliege werfen, nur weil genügend Erde vorrätig
ist. Es war eigentlich nur ein neugieriger Versuch, ob das so auch gehen würde.
Also nochmals vielen Dank, die Denkanstöße haben mir trotzdem geholfen, manche schon
im Vorgriff auf kommende Projekte.
Dateien öffnet man immer mit Hilfe des with-Statements.
Statt der while-Schleife benutzt man eine for-Schleife.
Und Du hast jetzt genau den Fehler gemacht, vor dem wir hier die ganze Zeit warnen. Zeichen können aus mehreren Bytes bestehen, so dass die Länge eines Strings nicht der Anzahl der Bytes in der Datei entspricht.
Du mußt im Binären Modus arbeiten.
Statt der while-Schleife benutzt man eine for-Schleife.
Und Du hast jetzt genau den Fehler gemacht, vor dem wir hier die ganze Zeit warnen. Zeichen können aus mehreren Bytes bestehen, so dass die Länge eines Strings nicht der Anzahl der Bytes in der Datei entspricht.
Du mußt im Binären Modus arbeiten.
Code: Alles auswählen
# Testfile erzeugen
with open('text', 'w') as file:
file.write('Hallo\n')
file.write('Du wohnst\n')
file.write('ÄÖÜäöüß[]{}€#><*\n')
file.write('zu Hause\n')
file.write('Und Du bist\n')
file.write('hier der Chef!\n')
# Index für Testfile aufbauen
with open('text', 'rb') as file:
posindex = [0]
for zeile in file:
posindex.append(file.tell())
# Zeigen der Indexliste
print(posindex)
# Heraussuchen einer vorgebenen Zeile
suchpos = 2
with open('text', 'rb') as file:
file.seek(posindex[suchpos])
ergebnis = file.read(posindex[suchpos+1] - posindex[suchpos]).decode()
print(file.tell())
print(f'[{ergebnis[:-1]}]')
-
- User
- Beiträge: 29
- Registriert: Montag 21. Februar 2011, 07:37
Das mit dem with-Statement ist schon klar, war 'ne alte Gewohnheit mit open. Du hast meinen Kniff nicht vollständig wahrgenommen. Wie Du siehst, ist in der Datei z.B. auch das mehrbytige €-Zeichen und es funktioniert doch, weil ich nämlich genau die einzelnen Bytes garnicht adressiere sondern immer nur in die '\n' einraste. Dadurch wird nach read() aus der Zeile ein String, in dem ich wieder klassisch lesend herummarschieren kann. Das habe ich alles deshalb so gemacht, weil ich natürlich genau Deinen Hinweis beachtet habe und die angedrohten Auswirkungen vermieden habe. Ich habe natürlich das "falsche Vorgehen" ausprobiert, um zu sehen, was passiert und genau, wie Du es vorausgesagt hast, hat es genau dann geknallt, wenn ich eines der Mehrfachbytes anzusteuern versuchte. Keine Sorge, ich bin kein Ignorant, der gutgemeinte Hinweise einfach in den Wind schlägt.
Habe es übrigens ausprobiert, ob es auch mit Unicode-Zeichen geht, z.B. '√ ' und es wird geliefert, natürlich nur untergebracht im String, wie oben beschrieben. Mir ist jetzt auch an Deinem Beispiel richtig bewusst geworden, dass das with ohne close() auskommt.
Habe es übrigens ausprobiert, ob es auch mit Unicode-Zeichen geht, z.B. '√ ' und es wird geliefert, natürlich nur untergebracht im String, wie oben beschrieben. Mir ist jetzt auch an Deinem Beispiel richtig bewusst geworden, dass das with ohne close() auskommt.
-
- User
- Beiträge: 29
- Registriert: Montag 21. Februar 2011, 07:37
Nun habe ich beim Herumprobieren endlich noch bemerkt. welche vielen kleinen Verbesserungen neu hinzugekommen sind. Danke dafür.
-
- User
- Beiträge: 29
- Registriert: Montag 21. Februar 2011, 07:37
Als ich Deine vielen kleinen Verbesserungen entdeckt habe, habe ich zuerst gesehen, dass es so viel eleganter ist. Aber inzwischen wundere ich mich selber, dass es bei mir den Anschein hatte, dass meine Version funktionieren würde.