Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 278 – Universelle Zeilenende-Unterstützung

Autor:
Jack Jansen <jack at cwi.nl>
Status:
Final
Typ:
Standards Track
Erstellt:
14. Januar 2002
Python-Version:
2.3
Post-History:


Inhaltsverzeichnis

Zusammenfassung

Diese PEP diskutiert eine Methode, wie Python Dateien mit Zeilenenden unterstützen kann, die nicht dem nativen Format der Plattform entsprechen, sodass Python auf jeder Plattform Dateien mit CR (Macintosh), LF (Unix) oder CR LF (Windows) Zeilenenden lesen und importieren kann.

Es wird immer häufiger, Dateien zu finden, die Zeilenenden haben, die nicht dem Standard der aktuellen Plattform entsprechen: Dateien, die über das Netz heruntergeladen wurden, entfernte Dateisysteme auf einer anderen Plattform, Mac OS X mit seinem doppelten Standard von Mac- und Unix-Zeilenenden usw.

Viele Werkzeuge wie Editoren und Compiler gehen bereits flexibel damit um, es wäre gut, wenn Python dies auch täte.

Spezifikation

Die universelle Zeilenende-Unterstützung ist standardmäßig aktiviert, kann aber während der Konfiguration von Python deaktiviert werden.

In einem Python mit universeller Zeilenende-Unterstützung ist die Funktion automatisch für alle Importanweisungen und execfile() Aufrufe aktiviert. Es gibt keine spezielle Unterstützung für eval() oder exec.

In einem Python mit universeller Zeilenende-Unterstützung kann der Modusparameter von open() auch "U" sein, was bedeutet "zum Lesen als Textdatei mit universeller Zeilenende-Interpretation öffnen". Der Modus "rU" ist ebenfalls zulässig, zur Symmetrie mit "rb". Der Modus "U" kann nicht mit anderen Modusflags wie "+" kombiniert werden. Jedes Zeilenende in der Eingabedatei wird in Python als '\n' betrachtet, sodass wenig anderer Code geändert werden muss, um universelle Zeilenenden zu unterstützen.

Die Konvertierung von Zeilenenden erfolgt bei allen Aufrufen, die Daten lesen: read(), readline(), readlines() usw.

Es gibt keine spezielle Unterstützung für die Ausgabe in Dateien mit einer anderen Zeilenenden-Konvention, daher ist der Modus "wU" ebenfalls ungültig.

Ein Dateiobjekt, das im Modus für universelle Zeilenenden geöffnet wurde, erhält ein neues Attribut "newlines", das die in der Datei verwendete Zeilenenden-Konvention widerspiegelt. Der Wert dieses Attributs ist einer von None (noch kein Zeilenende gelesen), "\r", "\n", "\r\n" oder ein Tupel, das alle gesehenen Zeilenendtypen enthält.

Begründung

Die universelle Zeilenende-Unterstützung ist in C implementiert, nicht in Python. Dies geschieht, weil wir möchten, dass Dateien mit einer fremden Zeilenenden-Konvention importierbar sind, sodass ein Python Lib-Verzeichnis über eine Remote-Dateisystemverbindung oder zwischen MacPython und Unix-Python auf Mac OS X geteilt werden kann. Damit dies machbar ist, muss die universelle Zeilenende-Konvention einen relativ geringen Einfluss auf die Leistung haben, was bedeutet, dass eine Python-Implementierung keine Option ist, da sie alle Imports verlangsamen würde. Und aufgrund von Dateien mit mehreren Zeilenenden-Konventionen, die Visual C++ und andere Windows-Tools gerne produzieren, funktioniert eine schnelle Überprüfung der in einer Datei verwendeten Zeilenenden (und die Übergabe des Imports an C-Code, wenn ein plattformspezifisches Zeilenende gesehen wird) nicht. Schließlich ermöglicht eine C-Implementierung auch die einfache Handhabung von Tracebacks und ähnlichem (die das Python-Quellmodul öffnen).

Es gibt keine Ausgabeimplementierung für universelle Zeilenenden. Python-Programme werden erwartet, dies selbst zu handhaben oder andernfalls Dateien mit plattformspezifischen Konventionen zu schreiben. Der Grund dafür ist, dass die Eingabe der schwierige Fall ist, die Ausgabe unterschiedlicher Zeilenenden in eine Datei in Python bereits einfach genug ist.

Außerdem wäre eine Ausgabeimplementierung überraschenderweise viel schwieriger als eine Eingabeimplementierung: Viel Ausgabe erfolgt über PyXXX_Print() Methoden, und zu diesem Zeitpunkt ist das Dateiobjekt nicht mehr verfügbar, nur ein FILE *. Eine Ausgabeimplementierung müsste also irgendwie vom FILE* zum Dateiobjekt gelangen, da dort der aktuelle Zeilenendbegrenzer gespeichert ist.

Die Eingabeimplementierung hat kein solches Problem: Es gibt im Python-Quellcode keine Fälle, in denen Dateien teilweise aus C und teilweise aus Python gelesen werden, und solche Fälle werden voraussichtlich in Erweiterungsmodulen selten sein. Wenn solche Fälle auftreten, ist das einzige Problem, dass das newlines-Attribut des Dateiobjekts während der fread() oder fgets() Aufrufe, die direkt aus C erfolgen, nicht aktualisiert wird.

Eine partielle Ausgabeimplementierung, bei der Strings, die an fp.write() übergeben werden, so konvertiert werden, dass fp.newlines als Zeilenende verwendet wird, während alle anderen Ausgaben dies nicht tun, ist meiner Meinung nach viel zu überraschend.

Da es keine Ausgabeunterstützung für universelle Zeilenenden gibt, gibt es auch keine Unterstützung für den Modus "rU+": Der Überraschungseffekt des vorherigen Absatzes würde in noch stärkerem Maße zutreffen.

Es gibt keine Unterstützung für universelle Zeilenenden in Strings, die an eval() oder exec übergeben werden. Es wird davon ausgegangen, dass solche Strings immer den standardmäßigen \n Zeilenvorschub haben. Wenn die Strings aus einer Datei stammen, kann diese Datei mit universellen Zeilenenden gelesen werden.

Ich glaube, es gibt keine besonderen Probleme mit Unicode. UTF-16 sollte keine neuen Probleme bereiten, da solche Dateien ohnehin im Binärmodus geöffnet werden müssen. Die Interaktion mit UTF-8 ist ebenfalls gut: Die Werte 0x0a und 0x0d können nicht Teil einer Mehrbyte-Sequenz sein.

Dateien mit universellen Zeilenenden sollten gut mit Iteratoren und xreadlines() funktionieren, da diese letztendlich die normalen Datei-readline/readlines-Methoden aufrufen.

Während universelle Zeilenenden für den Import automatisch aktiviert sind, sind sie es nicht für das Öffnen, wo man explizit open(..., "U") sagen muss. Dies ist diskussionswürdig, aber hier sind einige Gründe für dieses Design:

  • Kompatibilität. Programme, die bereits ihre eigene Interpretation von \r\n in Textdateien durchführen, würden kaputtgehen. Beispiele für solche Programme wären Editoren, die Sie warnen, wenn Sie eine Datei mit einer anderen Zeilenenden-Konvention öffnen. Wenn universelle Zeilenenden zur Standardeinstellung gemacht würden, würde ein solcher Editor Ihre Zeilenenden beim Speichern stillschweigend in die lokale Konvention umwandeln. Programme, die Binärdateien unter Unix als Textdateien öffnen, würden ebenfalls kaputtgehen (aber man könnte argumentieren, dass sie es verdienen :-)).
  • Klarheit der Schnittstelle. Universelle Zeilenenden werden nur für Eingabedateien unterstützt, nicht für Eingabe-/Ausgabedateien, da die Semantik unklar werden würde. Würden Sie Mac-Zeilenenden schreiben, wenn alle bisherigen Lesevorgänge Mac-Zeilenenden angetroffen hätten? Aber was, wenn Sie später einen Unix-Zeilenumbruch lesen?

Das newlines Attribut ist enthalten, damit Programme, denen die Zeilenenden-Konvention wirklich wichtig ist, wie z.B. Texteditoren, untersuchen können, was in einer Datei war. Sie können dann (eine Kopie der) Datei mit der gleichen Zeilenenden-Konvention speichern (oder im Falle einer Datei mit gemischten Zeilenenden den Benutzer fragen, was zu tun ist, oder in der plattformspezifischen Konvention ausgeben).

Feedback wird ausdrücklich zu einem Punkt in der Referenzimplementierung erbeten: Ob die Routinen für universelle Zeilenenden die globale Interpreter-Sperre greifen sollen oder nicht. Derzeit tun sie dies nicht, aber dies könnte als gefährlich angesehen werden, da sie Felder in einem FileObject modifizieren könnten. Da diese Routinen jedoch auch Ersatz für fgets() und fread() sind, kann es schwierig sein zu entscheiden, ob die Sperre gehalten wird oder nicht, wenn die Routine aufgerufen wird. Darüber hinaus besteht die einzige Gefahr darin, dass, wenn zwei Threads gleichzeitig dasselbe FileObject lesen, ein zusätzlicher Zeilenumbruch gesehen werden könnte oder das newlines Attribut versehentlich auf "gemischt" gesetzt werden könnte. Ich würde argumentieren, dass, wenn Sie dasselbe FileObject gleichzeitig in zwei Threads lesen, Sie sowieso nach Ärger suchen.

Beachten Sie, dass keine global zugänglichen Zeiger in den fgets() oder fread() Ersatzroutinen manipuliert werden, nur einige ganzzahlige Flags, daher ist die Wahrscheinlichkeit von Core-Dumps null (sagte er:-)).

Die universelle Zeilenende-Unterstützung kann während der Konfiguration deaktiviert werden, da sie eine geringe Leistungseinbuße hat und die Implementierung noch nicht auf allen denkbaren Plattformen getestet wurde. Sie könnte auf einigen Plattformen auch unsinnig sein (z.B. WinCE oder Palm-Geräte). Wenn die universelle Zeilenende-Unterstützung nicht aktiviert ist, haben Dateiobjekte nicht das newlines Attribut. Das Testen, ob das aktuelle Python dies hat, kann mit einem einfachen

if hasattr(open, 'newlines'):
   print 'We have universal newline support'

Beachten Sie, dass dieser Test die open() Funktion anstelle des file Typs verwendet, damit er nicht fehlschlägt für Python-Versionen, in denen der file Typ nicht verfügbar war (der file Typ wurde im selben Release wie die universelle Zeilenende-Funktion zum integrierten Namensraum hinzugefügt).

Zusätzlich beachten Sie, dass dieser Test bei Python-Versionen >= 2.5 fehlschlägt, als open() wieder eine Funktion wurde und nicht mehr synonym mit dem file Typ ist.

Referenzimplementierung

Eine Referenzimplementierung ist im SourceForge Patch #476814 verfügbar: https://bugs.python.org/issue476814

Referenzen

Keine.


Quelle: https://github.com/python/peps/blob/main/peps/pep-0278.rst

Zuletzt geändert: 2025-02-01 08:55:40 GMT