Dateien - In Zeile springen

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.
Antworten
quinto117
User
Beiträge: 5
Registriert: Donnerstag 26. Juli 2012, 22:24

Hallo

Ich weiss, dass ich die Schreibposition mit seek() ändern kann

Code: Alles auswählen

open('C:/B/A.txt', 'r+').seek(42)
Damit springe ich ja zum 42. Zeichen im Text. Meine Frage ist, ob ich auch direkt auf Zeilenzahlen springen kann?
Also kann ich mit nur einer Programmzeile zur Dokumentzeile 3 springen oder bleiben mir da nur Schleifen übrig?
BlackJack

@quinto117: Bei Dateien die im Textmodus geöffnet wurden wäre ich mit `seek()` sehr vorsichtig. Das muss nicht das 42te Zeichen sein zu dem da gesprungen wird. Im Grunde ist nur garantiert das man mit `seek()` zu einer Stelle gehen kann die man vorher mal mit `tell()` ermittelt hat. Was die Zahl konkret bedeutet ist nicht definiert.

Und zu einer bestimmten Zeile kann man nicht springen weil man dafür ja wissen müsste wo die Zeilen jeweils anfangen in der Datei. Diese Information wird aber nirgends gespeichert. Es läuft also auf Schleifen hinaus.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@quinto117:
Vllt. hilft Dir linecache weiter.

Für eine genaue Positionierung in Textmodus-Dateien ist `seek` gänzlich ungeeignet. Das Problem erbt CPython quasi von C, welches durch verschiedene line endings der Plattformen und mögliche Dateipuffermanipulationen (`getc` und `ungetc`) komplett falsche Werte in `ftell` bzw. unerwartete Postionen mit `fseek` liefern kann.

Mit dem `for line in f`-Idiom kommt in Python noch ein weiterer Puffer hinzu, kA ob `f.tell()` um diesen korrigiert ist.
quinto117
User
Beiträge: 5
Registriert: Donnerstag 26. Juli 2012, 22:24

Linecache hilft mir leider nicht weiter, aber danke für den Hinweis.

Ich hab das mal so gelernt, dass man mit seek() zur Byteposition des Textes springt, bei einem einfachen ASCII-Text ist ja ein Byte ein Zeichen.

Des Weiteren wollte ich mal eine kleine Schleife machen, ich schaffs aber irgendwie nicht.

Code: Alles auswählen

i=0; Umbrüche = 0; file=open('C:/B/A.txt', 'r+') 

while Umbrüche != 3:
	i += 1
	file.seek(i)
	if file.read(i) == '\n':
		Umbrüche += 1
Mit read(x) liest er anscheinend nicht die xte Position aus, ich habe da offenbar einen Verständnisfehler.

Soll ich das mit der Schreibposition ganz vergessen? Alternativ könnte ich ja mein Ziel auch erreichen wenn ich mit read() den gesamten Text auslese, ihn in Python erweitere / abändere und ihn dann das bestehende file überschreiben lasse.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@quinto117: was willst Du denn eigentlich machen? Ich habe bei Text-Dateien noch nie seek verwendet bzw. vermisst. Ich vermute, Du willst ein Problem lösen, das ganz anders gelöst werden sollte.
BlackJack

@quinto117: Das hast Du wahrscheinlich in einer Zeit gelernt als Texte noch Bytes waren. Wobei selbst dort unter Windows noch das Problem mit den Zeilenenden existiert wo die Byteanzahl in der Datei nicht der Byteanzahl nach dem Lesen bzw. vor dem Schreiben entspricht. Also stimmt auch bei einem einfachen ASCII-Text nicht das ein Byte ein Zeichen ist. Du hast da also etwas falsches gelernt.

``read(x)`` liest in der Tat nicht die x-te Position. Das sollte nach dem lesen der Dokumentation von `read()` aber auch klar sein warum das so ist. Und da Du einen Umlaut in einem Namen hast verwendest Du anscheinend Python 3 — wenn Du dort eine Datei nicht im Binärmodus öffnest, dann hast Du noch ganz andere Probleme denn dort wird dann automagisch beim Lesen dekodiert. Da könnte man je nach Kodierung mit einem `seek()` mitten in Zeichen springen die mit mehr als einem Byte kodiert sind. Das geht in so einem Fall dann in die Hose. Wenn Du das x-te *Byte* lesen möchtest, dann *musst* Du die Datei in Python 3 im Binärmodus öffnen.

Ansonsten schliesse ich mich Sirius3 an: Schildere doch mal das *Problem* das gelöst werden soll, und nicht das was Du für die Lösung hältst.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@quinto117:
Ok nochmal ausführlicher - im Binärmodus geöffnete Daten kann man mit seek/tell bearbeiten, das gilt auch für Textdateien. Im Textmodus geöffnete Dateien liefern unter Systemen mit Multiybyte-Zeilengrenze (z.B. Windows) falsche Werte mit seek. Da ist nur noch sichergestellt, dass eine mit tell ermittelte Position solange stimmt und mit seek angesprungen werden kann, wie nicht der Zeilenpuffer der Datei direkt manipuliert wurde (geht in C, Python nicht). POSIX-Systeme unterscheiden nicht zwischen Text-/Binär-Modus, technisch sind hier alle Dateien im Binärmodus (laut Spec eigentlich im Textmodus, was aber aufgrund der Einbyte-Zeilengrenze keine Auswirkungen hat). Eine weitere Problemquelle waren frühere Systeme, welche im Textmodus Zeichen mit einem Alignment (per 0x00-Padding) speicherten. ANSI C hat mit dem Textmodus diese Eigenheiten der Systeme abgefangen. Damit ist aber seek nicht mehr verlässlich, darum sieht die Spezifikation seek für den Textmodus nur nach tell vor, alles andere ist "undefined behavior". Mit heutigen Multibytekodierungen wie UTF8 wird das Navigieren und Ändern auf Byteebene noch schwieriger.

Hier mal etwas Code, um die Zeilenanfänge zu finden (ungetestet):

Code: Alles auswählen

def get_lineoffsets(f):
    f.seek(0, 2)
    end = f.tell()
    f.seek(0, 0)
    pos = 0
    while pos < end:
        yield pos
        f.readline()
        pos = f.tell()

with open('text', 'rb+') as f:
    for i, offset in enumerate(get_lineoffsets(f)):
        f.seek(offset, 0)
        # erstes Byte einer Zeile
        print i, repr(f.read(1))
        # überschreibe das 2. Byte
        f.write('Z')
Antworten