Ein- und Ausgaben von Daten

  1. Manuelle Eingaben
  2. Bildschirmausgabe
  3. Dateien lesen und schreiben

Eingabe von der Tastatur

Der wichtigste zu beachtende Punkt ist, dass alle Daten, die über die Tastatur eingelesen werden, automatisch vom Typ String sind:

eingabe = input("Bitte eine Zahl eingeben: ")
print(type(eingabe))
#print(eingabe+100)  # gibt einen Fehler, da eingabe vom Typ str
Zahl = int(eingabe)
print(Zahl+100)

Zahl = int(input("Bitte noch eine Zahl: "))
print(Zahl+150)

Vergisst man die Eingabe in eine Zahl zu wandeln (dritte Zeile in obigem Beispiel ist aktiv!), so ergibt sich folgende Fehlermeldung

# Hinweis
TypeError                                 Traceback (most recent call last)
 in 
      1 eingabe = input("Bitte eine Zahl eingeben: ")
      2 print(type(eingabe))
----> 3 print(eingabe+100)  # gibt einen Fehler, da eingabe vom Typ str
      4 Zahl = int(eingabe)
      5 print(Zahl+100)

TypeError: must be str, not int

Da interaktive Eingaben mit Python Online nicht möglich sind, muss das Beispiel auf dem eigenen Rechner oder mit Jupyter Lab durchgeführt werden.

Dadurch, dass die Eingabe grundsätzlich als String (Zeichenkette) aufgefasst wird, muss zwingend an eine nachträgliche Auswertung gedacht werden, falls dies erforderlich ist. Die Eingabe von "15/2" wird nur dann als "7.5" aufgefasst, wenn mit der Funktion eval() eine entsprechende Evaluation angefordert wird:

# Hinweis
eingabe = input("Bitte einen mathematischen Ausdruck eingeben: ")# 12/2
print(eingabe)
print(eval(eingabe))
neue_eingabe = eval(input("Bitte nochmal: "))# beispielsweise 127/2+17*3
print(neue_eingabe)

Da interaktive Eingaben mit Python Online nicht möglich sind, muss das Beispiel auf dem eigenen Rechner oder mit Jupyter Lab durchgeführt werden.

Bei der Eingabe einer Liste muss die eval-Funktion ebenfalls benutzt werden, denn die Umwandlung der String-Eingabe mit list führt zu einem falschen Ergebnis:

# Hinweis
NN = input("Nachnamen als Liste: ")# ["Müller", "Meier", "Schulze", "Krachulke"]
print(NN)
NN_liste = list(NN)#  fehlerhafte Zerlegung
print(NN_liste)

Da interaktive Eingaben mit Python Online nicht möglich sind, muss das Beispiel auf dem eigenen Rechner oder mit Jupyter Lab durchgeführt werden.

Die komplette Zerlegung der EIngabe kann verhindert werden, wenn wieder mit eval() gearbeitet wird. Es lassen sich dann auch Dictionaries einlesen:

# Hinweis
NN = eval(input("Nachnamen als Liste: "))# ["Müller", "Meier", "Schulze", "Krachulke"] # list
print(NN)
NV = eval(input("Nachname:Vorname als Dictionary: "))
# beispielsweise: {"Müller":"Johann", "Meier":"Corni", "Schulze":"Ernestine", "Krachulke":"Paule"} # dict
print(NV)

Da interaktive Eingaben mit Python Online nicht möglich sind, muss das Beispiel auf dem eigenen Rechner oder mit Jupyter Lab durchgeführt werden.

Ausgabe auf dem Bildschirm

Die Funktion print() gibt ihr Argument mit einem abschließenden Zeilenende/-vorschub aus. Werden mehrere Argumente durch Komma getrennt angegeben, so werden diese durch Leerzeichen getrennt ausgegeben. Eine Formatierung im eigentlichen Sinne findet nicht statt, alle Variablen werden in ihrer vorgegebenen Genauigkeit, bzw. Länge ausgegeben. Möchte man zum einen das Zeilenendezeichen unterdrücken oder statt der Leerzeichen ein Komma als Trenner ausgeben, so ist dies mit dem Schlüsselwort end=..., beziehungsweise sep="..." (separation) zu erreichen.

Syntax

# Hinweis
print(objects, sep=<separator>, end=<end>, file=<file>, flush=<True|False>)

Parameter Values

Parameter Description
object(s) Any object, and as many as you like. Will be converted to string before printed
sep=' separator ' Optional. Specify how to separate the objects, if there is more than one. Default is ' '
end=' end ' Optional. Specify what to print at the end. Default is '\n' (line feed)
file Optional. An object with a write method. Default is sys.stdout
flush Optional. A Boolean, specifying if the output is flushed (True) or buffered (False). Default is False

import math
print("print", "Ausgabe")
print("print"+"Ausgabe")
print(math.pi, math.pi/2, int(math.pi), math.pi/(math.pi/3))

print("Wert: ",end="")# kein Zeilenvorschub
print(math.e)# Ausgabe in derselben Zeile

print(math.pi, math.pi/2, int(math.pi), math.pi/(math.pi/3),sep=",")

Für formatierte Ausgaben kann man die Funktion "Text {x0:var} Text {x1:var} ... {...}".format.(), wobei hier x0 x1 ... für die jeweiligen Variablen (erste, zweite, ...) und var für die Art der Formatierung steht. Ein Beispiel wäre:

x = 1234/4321
y = 1/3
print("Standardausgabe mit fünf Nachkommastellen:")
print("x: {0:f} x: {0:f} y: {1:f}".format(x, y))# doppelte Ausgabe ist möglich
print()

print("Ausgabe mit definierten Nachkommastellen:")
print("{0:15.10f} {1:.25f}".format(x ,y))
print("----5---10---15 -.----5---10---15---20---25")
print()

print("Standardausgabe in Exponentialschreibweise:")
print("{0:e}".format(x))
print()

print("Exponentialdarstellung: {0:12.3e}".format(x))
print("                        ----5---10--")
print("{0:.3e}".format(x))  # drei Nachkommastellen
print()

print("{0:%}".format(y))    #     Standardausgabe mit Multiplikation von 100 und anschließendem %-Zeichen
print("{0:12.3%}".format(y))# 
print("----5---10--")       # 12 Stellen
print("{0:.3%}".format(y))  # 3 Nachkommastellen

Die Bedeutung der Formatierungszeichen lässt sich an Beispielen leicht erkennen: {1:15.10f} die zweite Variable in der folgenden Liste (Platznummer 1) wird im Bereich von 15 Stellen rechtsbündig mit 10 Nachkommastellen gesetzt; {0:12.3e} die erste Variable (Platznummer 0) wird im Bereich von 12 Stellen rechtsbüngig mit drei Nachkommastellen und exponentieller Darstellung gewählt, was einer Stelle vor dem Komma entspricht mit einer zweistelligen Zehnerpotenz. Die Formatierung von Zeichenketten (Strings) erfolgt ähnlich. Das Beispiel aus der ersten Einheit kann dann formatiert ausgegeben werden:

gehaltsliste = {
    "Müller"        : 3414.15,
    "Meyer"         : 1414.12,
    "Schnasebeutel" : 2452.03,
    "Krachulke"     : 3312.89,
    "Schulze"       : 2342.12 }
sortedListe = dict(sorted(gehaltsliste.items()))#  nur für Python Version > 3.6
i = 0
print("Die Gehaltsliste\n")
print("Nr. {0:<14} {1:<7} {2:<5}".format("Name", "Gehalt", "Gerundet"))
print("-----------------------------------")
for key, val in sortedListe.items():
  i += 1
  print("{0:2d}. {1:<14}{2:5.2f}€ {3:>6}".format(i, key, val, int(val)))

Für komplexen Programmcode kann man auch bei der Formatierung mit dem key-value-System arbeiten. Die Zuordnung zwischen Formatierung und Variable ist dann etwas einfacher zu erkennen:

gehaltsliste = {
    "Müller"        : 3414.15,
    "Meyer"         : 1414.12,
    "Schnasebeutel" : 2452.03,
    "Krachulke"     : 3312.89,
    "Schulze"       : 2342.12 }
sortedListe = dict(sorted(gehaltsliste.items()))#  nur für Python Version > 3.6
i = 0
print("Die Gehaltsliste\n")
print("Nr. {0:<14} {1:<7} {2:<5}".format("Name", "Gehalt", "Gerundet"))
print("-----------------------------------")
for key, val in sortedListe.items():
  i += 1
  print("{lfdNr:2d}. {NN:<14}{Voll:5.2f}€ {Gerundet:>6}".format(lfdNr=i, NN=key, Voll=val, Gerundet=int(val)))

file – Dateien

Datei-Objekte stellen in Python die Hauptschnittstelle zu externen Dateien auf dem Computer oder dem Netz dar. Sie können genutzt werden, um Dateien beliebigen Typs zu lesen oder zu schreiben. Datei-Objekte werden erzeugt, indem man die Funktion open() aufruft, und dabei den Namen der Datei sowie ein Kürzel für den gewünschten Bearbeitungsmodus angibt:

# Hinweis
myfile = open('file.txt', 'r')#   muss im aktuellen Verzeichnis des Web-Servers sein!!!

Als Bearbeitungsmodus kann 'r' (lesen), 'w' (schreiben) oder 'r+' (lesen und schreiben) gewählt werden. Sollen binäre Dateien gelesen beziehungsweise geschrieben werden, muss an das jeweilige Kürzel ein b angehängt werden, beispielsweise bezeichnet 'rb' den Lesemodus einer binären Datei.

Ein Beispiel:

import os
print("-- Aktuelles Verzeichnis auf dem Server --")
print(os.getcwd())
print("------------------------------------------")

#File Objects
file = open("/tmp/test.txt", "w")#  Verzeichnisse in Unix-Notation, oder "\\tmp\\test.txt" bei Windows
print("Objektname:",file.name)#   Object name
print("Dateimodus:",file.mode)#   Object mode "w"
file.write("HUHU, ich sitze in der FU")
file.write("\n")
file.close()
print()
print("-- .txt-Dateien in /tmp/ --")
for file in sorted(os.listdir("/tmp")):
    if file.endswith(".txt"):
        f = os.path.join("/tmp", file) 
        #print(os.path.getsize(f),f)
        print("{:>7}: {:<14}".format(os.path.getsize(f),f))

Die einzelnen Dateioptionen

Unterschiede zwischen r, r+, w, w+, a and a+ für open()
rr+ww+aa+
read ** * *
write * ** **
create ** **
delete **
position at start** **
position at end **

Hinweise

with open('/tmp/test.txt') as file:  # default `r` mode
    print(file.read())

Im r-Modus können keine Daten geschrieben werden; es gibt eine Fehlermeldung: io.UnsupportedOperation: not writable

with open('/tmp/test2.txt', 'r') as file:  # default `r` mode
    file.write("Test \n")            # throws UnsupportedOperation

Im r+ Modus ist Schreiben erlaubt! Zu beachten ist aber, dass der Dateizeiger durch den r-Modus am Anfang der Datei steht und somit ein direkt folgender Schreibbefehl den Anfang der Datei überschreibt!

import os

with open('/tmp/test2.txt', 'r') as f:   # Inhalt vorher
    print(f.read())

with open('/tmp/test2.txt', 'r+') as f:
    f.write("-------\n")                   # 7 Zeichen

with open('/tmp/test2.txt', 'r') as f:   # Inhalt nachher
    print(f.read())
##
##  Damit Test wiederholt werden kann, wird test2.txt wieder auf das Original gesetzt
open("/tmp/test2.txt", "wb").write(open('/tmp/test2-orig.txt', 'rb').read())
for file in sorted(os.listdir("/tmp")):
    if file.endswith(".txt"):
        f = os.path.join("/tmp", file) 
        #print(os.path.getsize(f),f)
        print("{:>7}: {:<14}".format(os.path.getsize(f),f))

Abhilfe schafft das Setzen des Dateizeigers (pointer) auf das Ende der Datei, was man mit seek(...) erreichen könnte, wenn der Inhalt der Datei bekannt wäre. Einfacher ist es, einfach alles einzulesen:

import os
with open('/tmp/test2.txt', 'r+') as f: # r+ -> Lesen/Schreiben
    f.read()                            # Dateizeiger steht jetzt am Ende der Datei.
    f.write("-------\n")
    f.seek(0)                           # zurück an den Anfang
    print(f.read())
##
##  Damit Test wiederholt werden kann, wird test2.txt wieder auf das Original gesetzt
open("/tmp/test2.txt", "wb").write(open('/tmp/test2-orig.txt', 'rb').read())
for file in sorted(os.listdir("/tmp")):
    if file.endswith(".txt"):
        f = os.path.join("/tmp", file) 
        #print(os.path.getsize(f),f)
        print("{:>7}: {:<14}".format(os.path.getsize(f),f))

Unterschied zwischen w und w+

import os
with open('/tmp/test2.txt', 'w') as f:   # create a new or delete it
    f.write("test 1\n")
    f.write("test 2\n")
    f.write("test 3\n")
    f.seek(0)              # move the file pointer to the beginning
    lines = f.read()       # read it, but we get an error with 'w' not readable!!!
    print(lines)           # old data gets lost
##
##  Damit Test wiederholt werden kann, wird test2.txt wieder auf das Original gesetzt
open("/tmp/test2.txt", "wb").write(open('/tmp/test2-orig.txt', 'rb').read())
for file in sorted(os.listdir("/tmp")):
    if file.endswith(".txt"):
        f = os.path.join("/tmp", file) 
        #print(os.path.getsize(f),f)
        print("{:>7}: {:<14}".format(os.path.getsize(f),f))

Dasselbe Beispiel mit w+

import os
with open('/tmp/test2.txt', 'w+') as f:   # create a new or delete it
    f.write("test 1\n")
    f.write("test 2\n")
    f.write("test 3\n")
    f.seek(0)              # move the file pointer to the beginning
    lines = f.read()       # read it, now we can read!
    print(lines)           # old data gets lost
##
##  Damit Test wiederholt werden kann, wird test2.txt wieder auf das Original gesetzt
open("/tmp/test2.txt", "wb").write(open('/tmp/test2-orig.txt', 'rb').read())
for file in sorted(os.listdir("/tmp")):
    if file.endswith(".txt"):
        f = os.path.join("/tmp", file) 
        #print(os.path.getsize(f),f)
        print("{:>7}: {:<14}".format(os.path.getsize(f),f))

Unterschied zwischen a und a+

with open('test.txt','w') as f:
    f.write("welcome to python 1\nwelcome to python 2\nwelcome to python 3\nwelcome to python 4\n")

with open('test.txt', 'a') as  f: # a -> Anhängen (append)
    f.read()            # gibt einen Fehler!! io.UnsupportedOperation: not readable
    f.write("-------\n")

import os
with open('test.txt','w') as f:
    f.write("welcome to python 1\nwelcome to python 2\nwelcome to python 3\nwelcome to python 4\n")

with open('test.txt', 'a+') as  f: # a -> Lesen/Anhängen (append)
    f.read()            # gibt jetzt keinen Fehler!!
    f.write("-------\n")

with open('test.txt', 'r+') as f: # r+ -> Lesen/Schreiben
    print(f.read())

for file in sorted(os.listdir("/tmp")):
    if file.endswith(".txt"):
        f = os.path.join("/tmp", file) 
        #print(os.path.getsize(f),f)
        print("{:>7}: {:<14}".format(os.path.getsize(f),f))

Der Unterschied zwischen r+ und w+, ist die Tatsache, dass die Datei bei r+ bereits existieren muss! w+ würde eine Datei erst erstellen, was sie nochn icht existiert.

Einlesen von Dateien

Wird eine Datei im Lesemodus geöffnet, so kann sie beispielsweise mittels der Funktion read() im Ganzen als ein einziger String eingelesen werden:

# Hinweis
# Datei als einzelnen String einlesen:
long_string = myfile.read()

Diese Methode ist für größere Dateien nicht empfehlenswert. Besser ist es, mittels der Funktion readline() eine Datei Zeile für Zeile einzulesen. Bei jedem solchen Aufruf wird die jeweils eingelesene Zeile als Ergebnis zurückgegeben und der „Cursor“ für die aktuelle Position in der Datei auf die nächste Zeile gesetzt. Mit der Funktion readlines() wird die Datei zeilenweise in eine Liste von Zeichenketten eingelesen. Noch einfacher ist ein zeilenweises Einlesen, indem die Datei-Variable selbst als iterierbares Objekt an eine for-Schleife übergeben wird:

with open("/tmp/goethe_werther.txt") as myfile:
  # Schleife über alle Zeilen der Datei:
  for line in myfile:
    # Gib die aktuelle Zeile aus:
    print(line)

Am Ende eines Lesezugriffs sollte die Datei mittels close(myfile) wieder geschlossen werden.

Das Einlesen von csv-Dateien (kommaseparierte Datenzeilen) wird in der 12. Einheit behandelt: CSV Data

Schreiben in Dateien

Um Text in eine Datei zu schreiben, wird diese zunächst im Schreibmodus geöffnet:

# Hinweis
myfile = open('file.txt', 'w')

Anschließend kann mittels der Funktion write() eine (gegebenenfalls auch mehrzeilige) Zeichenkette in die Datei geschrieben werden:

# Hinweis
myfile.write('Hallo Welt!\n')

Zudem kann mittels myfile.writelines(stringliste) kann eine Liste von einzelnen Zeichenketten in eine Datei geschrieben werden. Die einzelnen Zeichenketten werden bei write() als auch beiwritelines() geschrieben „wie sie sind“, es muss also bei Bedarf ein Zeilenende-Zeichen \n am Ende der zu schreibenden Zeichenkette(n) sein, damit auch in der geschriebenen Datei an der jeweiligen Stelle ein Zeilenumbruch erfolgt.

Am Ende eines Schreibzugriffs muss close(myfile) wieder geschlossen die Datei mittels werden, da nur dann das Datei-Attribut mtime („Modifikationszeit“) korrekt gesetzt wird. Da während des Einlesens oder Schreibens von Dateien prinzipiell auch mit Fehlern gerech- net werden muss, müsste stets mittels

# Hinweis
try ... except ... finally

ein Ausnahme-Handling eingebaut werden. Eine (bessere) Alternative hierzu beziehungsweise zum manuellen Schließen eines file-Objekts besteht darin, ein with-Statement mit folgender Syntax zu verwenden:

# Hinweis
with open("file.txt", 'r') as myfile:
    for line in myfile:
        print(line)

Mit dieser Variante wird die Datei automatisch geschlossen, auch wenn beim Lesen oder Schreiben ein Fehler aufgetreten ist.

Lesen und Schreiben externer Dateien

Über http oder ftp

Entfernte Dateien, die über einen Web- oder Dateiserver zugänglich sind, können ohne SSH-VErbindung (secure shell) gelesen werden. Lediglich das Schreiben ist ohne eine Verbindung nicht möglich.

from urllib.request import urlopen

for zeile in urlopen("http://latex.userpage.fu-berlin.de/Mandel2021.py"):
    print(zeile.decode('utf-8'),end="") #utf-8 or iso8859-1 or whatever 
    # bei neueren Rechnern immer utf-8 und dann reicht print(line)

Weitere Informationen zum Laden von externen Dateien findet man hier.

Lesen und schreiben über Username und Passwort

Vorausgesetzt wird hier natürlich ein vorhandener Account auf einem Rechner, beispielsweise der Zugang auf dem ZEDAT-Rechner, den jeder durch seinen Usernamen und Passwort hat. Das Problem vereinfacht sich etwas, wenn man die entferne Datei auf seinem Rechner zwischenspeichert, was hier mit scp (secure copy) erfolgt:

# pip3 install paramiko
from paramiko import SSHClient
# pip3 install scp
from scp import SCPClient
import os

client = SSHClient()
client.load_system_host_keys()
#client.load_host_keys('/home/.../.ssh/known_hosts')
#client.set_missing_host_key_policy(AutoAddPolicy())

User = "username"       
PassWord = "password"
client.connect('login.zedat.fu-berlin.de', username=User, password=PassWord)

with SCPClient(client.get_transport()) as scp:
   scp.get('public_html/Mandel2021.py','Mandel2021.py')
myfile = open('Mandel2021.py', 'r')
for line in myfile:
  print(line,end="")
client.close()

Lesen und Schreiben über SSH-key

Für diese sehr zu empfehlende Arbeitsweise müssen SSH-Schlüssel existieren und auch auf den jeweils entfernten Rechner übertragen werden. Informationen dazu findet man auf der ZEDAT. Hier wird nur ein Beispiel angegeben, welches nur dann funktionieren kann, wenn man es vom eigenen Rechner aus startet:

from paramiko import SSHClient
from scp import SCPClient

ssh = SSHClient()
ssh.load_system_host_keys()
#ssh.load_host_keys('/Users/voss/.ssh/known_hosts')
ssh.connect('remote-server')

with SCPClient(ssh.get_transport()) as scp:
    scp.put('test_2.py', 'testxyz.txt')
    scp.get('testxyz.txt')

Nächste Einheit: 06 Anwendungen