for line in quelldateiobjekt: >> und kein Ende mehr

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
m__golbs
User
Beiträge: 13
Registriert: Freitag 9. Juni 2023, 10:12

Hallo,

ich habe den Effekt, das ich auf eine ASCII Datei ein:

Code: Alles auswählen

quelldateiobjekt = open(namequelldatei, "r")
for line in quelldateiobjekt:
...
nutze, das Dateiende aber nicht gefunden wird, ewig läuft. Aktuell beende ich es mit schmutziger Option..

Innerhalb nutze ich noch, nach Option, andere Fileoperationen wie:

Code: Alles auswählen

	Ausgabe_dateiobjekt = open(Ausgabedateiname, "w")
	...
	Ausgabe_dateiobjekt.close()
	...
Das darf man doch grundsätzlich, oder doch nicht?

Gruß Markus
Benutzeravatar
__blackjack__
User
Beiträge: 14429
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@m__golbs: Wenn da kein Dateiende gefunden wird, dann hat die Datei kein Ende. Wenn sie ein Ende hat, dann wird das von so einer Schleife auch ”gefunden”, das heisst die Schleife endet dann.

Man darf in einer Schleife über die Zeilen einer Textdatei auch weitere Dateiobjekte verwenden. Was natürlich logischerweise zu einer Endlosschleife führen kann, ist wenn man in der Schleife über die Zeilen einer Textdatei an genau diese Textdatei mehr und schneller Zeilen anhängt als die umgebende Schleife liest. Denn diese Schleife kommt ja irgendwann auch zu den neu angehängten Zeilen und liest die.

Beim öffnen von Textdateien sollte man übrigens die Kodieurng explizit angeben.

Der Zusatz `objekt` ist in Namen in aller Regel nicht sinnvoll, denn in Python ist *jeder* Wert ein Objekt.

Ich sehe beim ersten Quelltextschnippsel kein ``with`` und beim Zweiten einen `close()`-Aufruf: Dateiobjekte sind Kontextmanager, die sollte man mit der ``with``-Anweisung verwenden. Also:

Code: Alles auswählen

    with open(quelldateiname, "r", encoding="ascii") as quelldatei:
        for zeile in quelldatei:
            ...
und

Code: Alles auswählen

    with open(ausgabedateiname, "w", encoding="ascii") as ausgabedatei:
        ...
    ...
“Design patterns are kind of like sarcasm: hard to use well, not always appropriate,
and disgustingly bad when applied to problems they are not meant to solve.” — ajones in c.l.python
Benutzeravatar
DeaD_EyE
User
Beiträge: 1367
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

m__golbs hat geschrieben: Donnerstag 23. Juli 2026, 13:34 nutze, das Dateiende aber nicht gefunden wird, ewig läuft.
Das kann eigentlich nicht passieren. Sobald du das Dateiende erreichst, wird StopIteration ausgelöst, was die For-Schleife beendet.

Angenommen ein anderer Prozess schreibt kontinuierlich Daten in die Datei und der Python-Prozess liest kontinuierlich, sollte er irgendwann so weit sein, dass er das Ende der Datei erreicht, bevor der andere Prozess die nächsten Daten geschrieben hat.

Dann gibt es noch Pseudo-Dateien, die kein Ende haben: /dev/random, /dev/zero, /dev/urandom

Das läuft unendlich lang:

Code: Alles auswählen

with open("/dev/zero", "rb") as fd:
    buffer = bytearray(1024)
    while size := fd.readinto(buffer):
        pass
Jetzt das ⚠ gefährliche ⚠ Beispiel, dass ziemlich schnell den RAM füllt:

Code: Alles auswählen

with open("/dev/zero", "rb") as fd:
    for line in fd:
        pass
Der Grund ist ein ganz einfacher. Die Pseudo-Datei /dev/zero liefert durchgehend 0-Bytes. Der iterator von fd konsumiert so lange Daten (die im RAM landen), bis das Zeilenende b"\n" gefunden worden ist. Da bei dieser speziellen Pseudo-Datei niemals ein Zeilenende vorkommt, läuft der RAM voll. Er erreicht auch niemals das Statement pass, da der Iterator von fd damit beschäftigt ist, den RAM zu füllen.

PS: Ich beziehe mich hier ausschließlich auf Linux.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Pedroski55
User
Beiträge: 60
Registriert: Freitag 25. Juli 2025, 00:20

Das Computerbetriebssystem hält Metadata für alle Dateien. Unter anderem, das Betriebsystem hält eine Rekorde der Länge, in Bytes, einer jeden Datei.

Code: Alles auswählen

file = '/home/peterr/temp/datasets.json'
meta_data = os.stat(file) # Dateigröße  = st_size=23430
meta_data sieht so aus:
os.stat_result(st_mode=33204, st_ino=2360998, st_dev=66311, st_nlink=1, st_uid=1000, st_gid=1000, st_size=23430, st_atime=1785194899, st_mtime=1755909298, st_ctime=1755909298)
Unter der Haube, unter der Schlangenhaut, Python ist auf C gebaut. C hat eine Funktion read()

readme

Einfach ausgedrückt, read() liest eine Anzahl bytes ein, und incrementiert offset, bis offset >= Filelänge, dann gibt read() 0 (Null, Zero) zurück, und die Leseoperation ist beendet.

Maximal, auf Linuxsystemen kann read() (2,147,479,552) bytes am Stück liefern.

Da eine Datei ohne Ende (st_size=∞,) die ganze Festplatte und mehr füllen wird, gehe ich davon aus dass deine Datei "endlich" ist. Du machst da irgendwas krummes in deiner Schleife! Wahrscheinlich eine while-Schleife ohne Ende.

Einfach dies eingeben:

Code: Alles auswählen

file_size_bytes = os.path.getsize(datei)
Dann weißt du wie groß die Datei ist!

Alles hat ein Ende, nur die Wurst hat zwei!
Benutzeravatar
sparrow
User
Beiträge: 4662
Registriert: Freitag 17. April 2009, 10:28

@Pedroski: Wie passen deine Ausführungen zu der von DeaD_EyE genannten Datei /dev/null?
Benutzeravatar
DeaD_EyE
User
Beiträge: 1367
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Das Limit kann man ausrechnen: 2**31 - 1 - (4 * 1024)
Binärdaten verarbeitet man am besten in Chunks und dann hat man auch kein Problem mit dem Limit.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
noisefloor
User
Beiträge: 4330
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

@Pedrowski: mal abgesehen vom sehr berechtigen Einwand von @sparrow hast du einen elementaren Denkfehler in deinem Post: es gibt Dateisystem wie Linux ext4, die gleichzeitiges Lesen und Schreiben von physischen Dateien erlauben. Schreibt man schneller als man liest, hat man erst Mal eine Endlosschleife, bis man das Dateigrößenlimit erreicht hat oder das Laufwerk voll ist. Wurde weiter oben in ähnlicher Form auch schon gesagt.

Natürlich kann du zum Zeitpunkt X dann auch Dateidaten wir Größe abfragen - aber die kann halt zum Zeitpunkt X+1 anders sein.

Gruß, noisefloor
Benutzeravatar
DeaD_EyE
User
Beiträge: 1367
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Offensichtlich wird das Limit bei Python umgangen:
>>> len(open("test4GiB.bin", "rb").read(4294967296))
4294967296

Ich vermute, dass CPython in chunks liest, um das Limit zu umgehen. Aber normalerweise macht man sowas nicht. Da wäre ein mmap auf die Datei besser.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
__blackjack__
User
Beiträge: 14429
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das Limit wird bei Python nicht umgangen, das ist einfach grösser als hier behauptet wird. Schon auf C-Ebene. Zu `size_t` gibt es in ``stdint.h`` ein `SIZE_MAX`-Makro, und bei meinem 64-Bit-Linux hat das den Wert 18.446.744.073.709.551.615. `read()` hat den Rückgabetyp `ssize_t`, also die vorzeichenbehaftete Variante von `size_t` und hat deshalb den halben Maximalwert: 9.223.372.036.854.775.807. Den gibt bei mir Python auch als `sys.maxsize` aus:

Code: Alles auswählen

In [10]: sys.maxsize
Out[10]: 9223372036854775807
“Design patterns are kind of like sarcasm: hard to use well, not always appropriate,
and disgustingly bad when applied to problems they are not meant to solve.” — ajones in c.l.python
Benutzeravatar
sparrow
User
Beiträge: 4662
Registriert: Freitag 17. April 2009, 10:28

Pedroski55 hat geschrieben: Dienstag 28. Juli 2026, 02:36 Einfach ausgedrückt, read() liest eine Anzahl bytes ein, und incrementiert offset, bis offset >= Filelänge, dann gibt read() 0 (Null, Zero) zurück, und die Leseoperation ist beendet.
Hier noch einmal die lange Form, warum das so nicht stimmt.
Nach deiner Aussage dürfte man aus /dev/urandom nie lesen dürfen.

Code: Alles auswählen

>>> import os
>>> file = "/dev/urandom"
>>> os.stat(file)
os.stat_result(st_mode=8630, st_ino=9, st_dev=7, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1785301379, st_mtime=1785301379, st_ctime=1785301379)
>>> os.path.getsize(file)
0
>>> len(open(file, "rb").read(500))
500
>>> 
Hier wurden erfolgreich 500 Bytes aus einer Datei gelesen, die eine Größe von 0 hat.
Sirius3
User
Beiträge: 18424
Registriert: Sonntag 21. Oktober 2012, 17:20

@__blackjack__: es macht einen Unterschied, wie die Python-Funktionen implementiert sind, wie die libc-Funktion implementiert ist, und wie das darunterliegende Interface zum Kernel.

Code: Alles auswählen

open("test4GiB.bin", "rb").read(4294967296)

Code: Alles auswählen

read(3, "\6\0\0\0"..., 4294967296) = 2147479552
read(3, "\231\245"..., 2147487744) = 2147479552
read(3, "\244\356"..., 8192) = 8192
Übrigens os.read reicht das Limit des Kernels direkt zum Python-Programmierer durch.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1367
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Sirius3 hat geschrieben: Mittwoch 29. Juli 2026, 10:52 @__blackjack__: es macht einen Unterschied, wie die Python-Funktionen implementiert sind, wie die libc-Funktion implementiert ist, und wie das darunterliegende Interface zum Kernel.

Code: Alles auswählen

open("test4GiB.bin", "rb").read(4294967296)

Code: Alles auswählen

read(3, "\6\0\0\0"..., 4294967296) = 2147479552
read(3, "\231\245"..., 2147487744) = 2147479552
read(3, "\244\356"..., 8192) = 8192
Übrigens os.read reicht das Limit des Kernels direkt zum Python-Programmierer durch.
CPython liest Dateien in Chunks. Die Funktion os.read() ist nur ein minimaler Wrapper um die C-Funktion und deswegen hat man dort auch das Limit des Linux-Kernels.
Der Datentyp ist AFIK ssize (im Linux Kernel) und 31 Bit stehen zur Verfügung. Die 4 KiB weniger kommen wahrscheinlich durch cache zustande.


Implementierung: https://github.com/python/cpython/blob/ ... eio.c#L875
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 18424
Registriert: Sonntag 21. Oktober 2012, 17:20

@DeaD_EyE: wie __blackjack__ ja schon schrieb, ist ssize_t auf üblichen 64bit-Systemen auch wirklich ein 64bit-int. Sonst könnte man ja 4294967296 gar nicht übergeben.
Und wie Du siehst, wird nicht in Chunks gelesen, sondern immer versucht, die komplette Größe zu lesen. Man sollte die Grenze von 2147479552 als Implementierungsdetail verstehen. Wenn man von Pipes, Sockets oder ähnlichem liest, hat man deutlich kleinere Grenzen.
Das Prinzip ist also: sag wieviel Du lesen willst, und wenn weniger geliefert wird, versuch es so lange weiter, bis die gewünschte Anzahl Bytes zusammengekommen sind, oder die Datei zu Ende ist.
Antworten