diff --git a/09kapitel/091 Dateien.ipynb b/09kapitel/091 Dateien.ipynb
new file mode 100644
index 0000000..a1d576b
--- /dev/null
+++ b/09kapitel/091 Dateien.ipynb
@@ -0,0 +1,404 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "# Software Entwicklung \n",
+ "\n",
+ "## Kapitel 9: IO\n",
+ "\n",
+ "Unter *IO* (Input/Output) versteht man alle Mechanismen,\n",
+ "die mit dem Ausleiten von Daten aus einem Programm\n",
+ "bzw. dem Einlesen von Daten in ein Programm zusammenhängen."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### 9.1 Zeichenkodierung\n",
+ "\n",
+ "Möchte ein Programm Daten senden (z.B. in eine Datei\n",
+ "oder über eine Netzwerkverbindung) oder diese empfangen,\n",
+ "werden letztendlich Bytes übertragen. Handelt es sich bei den\n",
+ "Daten um Text - also lesbare Zeichen - so muss durch eine\n",
+ "Zeichenkodierung (engl. *encoding*) festgelegt werden,\n",
+ "wie dieser Text auf Bytes abgebildet wird.\n",
+ "\n",
+ "Ein sehr übliches Kodierungsschema ist *UTF-8*. Darin werden\n",
+ "die Zahlen 0 bis 127 analog zum alten ASCII-Standard\n",
+ "insbesondere für grundlegende lateinische Zeichen in\n",
+ "Groß- und Kleinschrift verwendet. Alle übrigen Zeichen wie\n",
+ "z.B. deutsche Umlaute, chinesische Schriftzeichen,\n",
+ "Emojis etc. werden durch größere Zahlen ggf. mit mehreren\n",
+ "Bytes codiert. Die Zuordnung der Zahlen zu den Zeichen\n",
+ "ist durch den [*Unicode*-Standard](http://www.unicode.org/)\n",
+ "festgelegt."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Möchte man eine Python-Zeichenkette in eine Unicode-Bytefolge\n",
+ "übertragen, so kann dafür die Methode encode
der Klasse\n",
+ "*str* (String) verwendet werden."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "text = \"Nürnberg ist schön\"\n",
+ "print(f\"Länge {len(text)}\")\n",
+ "utf8 = text.encode(\"utf-8\")\n",
+ "print(utf8)\n",
+ "print(f\"Länge {len(utf8)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "source": [
+ "Das vorangestellte b
signalisiert, dass es sich\n",
+ "hier um eine Bytefolge handelt."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "print(type(utf8))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Eine solche Bytefolge kann auch wieder in einen\n",
+ "Textstring verwandelt werden. Dazu besitzt die\n",
+ "Klasse *bytes* die Methode decode
."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "decoded_text = utf8.decode(\"utf-8\")\n",
+ "print(decoded_text)\n",
+ "print(f\"Länge {len(decoded_text)}\")\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### 9.2 Dateien\n",
+ "\n",
+ "Unter *Textdateien* verstehen wir Dateien, die\n",
+ "mit einem Texteditor erzeugt und bearbeitet werden können.\n",
+ "Der Inhalt sind lesbare Zeichen, die meist\n",
+ "in Zeilen organisiert sind. Um sie auf einem\n",
+ "Speichermedium abzulegen, müssen die Zeichen\n",
+ "in Bytes konvertiert bzw. beim Lesen\n",
+ "rückübertragen werden.\n",
+ "\n",
+ "*Binärdateien* sind Dateien, für die keine Kodierung\n",
+ "stattfinden soll (z.B. Bilder).\n",
+ "\n",
+ "Python besitzt bereits im Standard Funktionen, Klassen und Methoden,\n",
+ "die das Lesen und Schreiben von Dateien sowie das Ãœbertragen\n",
+ "von Text in Bytes (und zurück) unterstützen."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Einstiegspunkt ist die Built-In-Funktion open
, die ein\n",
+ "Dateiobjekt zurückliefert. Die Parameter von open
sind\n",
+ "der *Dateiname* (ggf. inkl. Pfad) und der *Bearbeitungsmodus*\n",
+ "sowie weitere, weniger gebräuchliche optionale Parameter.\n",
+ "\n",
+ "\n",
+ "| *Bearbeitungsmodus* | *Bedeutung* |\n",
+ "|:--------------------:|----------------------------------------------------|\n",
+ "| 'r' | Die Datei wird gelesen (muss bereits existieren) |\n",
+ "| 'w' | Die Datei wird geschrieben (vorhandene Datei wird überschrieben) |\n",
+ "| 'a' | Die Datei wird geschrieben (neuer Inhalt wird angehängt) |\n",
+ "| 'r+' | Die Datei wird gelesen und geschrieben |\n",
+ "\n",
+ "Standardmäßig finden die Lese- und Schreiboperationen mit der Standard-Kodierung des verwendeten Betriebssystems statt. Soll die Kodierung unterbleiben, muss an den Bearbeitungsmodus ein 'b'
\n",
+ "(für binär) angehängt werden."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ },
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "f = open(\"091a Textdatei\", \"r\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Das auf diese Weise erhaltene Dateiobjekt kann für (je nach\n",
+ "Bearbeitungsmodus) für das Lesen und Schreiben benutzt werden.\n",
+ "Die Methode read
ohne Angabe eines Parameter\n",
+ "liest den gesamten Inhalt der Datei ein\n",
+ "(Vorsicht: nur bei kleinen Dateien ratsam, da speicherintensiv)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "content = f.read()\n",
+ "print(content)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Jedes geöffnete Dateiobjekt muss auch wieder geschlossen werden,\n",
+ "da andernfalls Betriebssystemressourcen dauerhaft belegt bleiben."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "print(f.closed)\n",
+ "f.close()\n",
+ "print(f.closed)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Sollte eine Textdatei in einer anderen Kodierung als die Standard-Kodierung vorliegen, kann die gewünschte Kodierung mittels des Parameters encoding
angegeben werden. Mit encoding=None
wird wieder die Standard-Kodierung gewählt, was dem Standardwert und damit dem Weglassen des Parameters entspricht. Im folgenden Beispiel wird eine Textdatei geladen, die im UTF-8-Format vorliegt. Die Standard-Kodierung auf Windows weicht allerdings davon ab, wie man an den Umlauten erkennen kann."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "f = open(\"utf8_text\", \"r\", encoding=None)\n",
+ "content = f.read()\n",
+ "print(content)\n",
+ "f.close()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "### 9.3 Kontextmanager\n",
+ "\n",
+ "Die strenge Verpflichtung, jede Datei auch zu schließen, ist\n",
+ "gelegentlich nicht einfach zu erfüllen. Falls z.B. eine\n",
+ "Exception auftritt und die Abarbeitungsreihenfolge\n",
+ "abgebrochen wird, ist trotzdem *close* aufzurufen."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ },
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " f = open(\"091a Textdatei\", \"r\")\n",
+ " z = 1 / 0\n",
+ " # f.close() würde nicht erreicht!\n",
+ "except:\n",
+ " print(\"Fehler!\")\n",
+ "finally:\n",
+ " f.close()\n",
+ "print(f.closed)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Python bietet für dieses Problem den *Kontextmanager* an. Er\n",
+ "initialisiert einen *Kontext* für ein Objekt, führt einen\n",
+ "Codeblock in diesem Kontext aus und räumt beim Verlassen\n",
+ "des *Kontext* belegte Ressourcen auf.\n",
+ "\n",
+ "Das Schlüsselwort für den Beginn eines *Kontext* ist with
."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"091a Textdatei\", \"r\") as f:\n",
+ " print(f.read())\n",
+ " print(f.closed)\n",
+ "\n",
+ "print(f.closed)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Wie ist das realisiert? Tatsächlich nutzt der\n",
+ "*Kontextmanager* die beiden *dunder*-Methoden\n",
+ "\\_\\_entry\\_\\_
und \\_\\_exit\\_\\_
.\n",
+ "Die Methode \\_\\_entry\\_\\_
wird beim Betreten des\n",
+ "Kontext aufgerufen, die Methode \\_\\_exit\\_\\_
beim\n",
+ "Verlassen oder beim Auftreten einer nicht behandelten Exception.\n",
+ "\n",
+ "Beim Datei-Objekt von Python wird in der\n",
+ "\\_\\_exit\\_\\_
-Methode sichergestellt, dass die\n",
+ "Datei geschlossen wird.\n",
+ "\n",
+ "Durch Überschreiben der Methoden können auch eigene Klassen\n",
+ "sinnvoll mit einem *Kontextmanager* zusammenarbeiten.\n",
+ "\n",
+ "\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Slideshow",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/09kapitel/091a Textdatei b/09kapitel/091a Textdatei
new file mode 100644
index 0000000..b0ba62c
--- /dev/null
+++ b/09kapitel/091a Textdatei
@@ -0,0 +1,9 @@
+1. Zeile
+2. Zeile
+3. etwas längere Zeile
+4. Zeile - hört das nie auf?
+5. Zeile - langsam wird es langweilig
+6. Zeile
+7. Zeile
+8. Zeile
+9. letzte Zeile
diff --git a/09kapitel/092 Lesen.ipynb b/09kapitel/092 Lesen.ipynb
new file mode 100644
index 0000000..d1ad126
--- /dev/null
+++ b/09kapitel/092 Lesen.ipynb
@@ -0,0 +1,215 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "# Software Entwicklung \n",
+ "\n",
+ "## Kapitel 9: IO\n",
+ "\n",
+ "### 9.4 Dateien lesen\n",
+ "\n",
+ "Bei großen Dateien sollte vom vollständigen Einlesen mittels\n",
+ "read
Abstand genommen werden, weil dann\n",
+ "der gesamte Dateiinhalt im Speicher gehalten werden muss.\n",
+ "Vielmehr sollte in diesem Fall der Inhalt sukzessive eingelesen\n",
+ "werden."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Zu diesem Zweck kann read
mit einem Parameter\n",
+ "aufgerufen werden, der die maximale Anzahl Bytes angibt,\n",
+ "die gelesen werden sollen."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"091a Textdatei\", \"r\") as f:\n",
+ " block_size = 10\n",
+ " block = f.read(block_size)\n",
+ "\n",
+ " while len(block)>0:\n",
+ " print(block, end='*')\n",
+ " block = f.read(block_size)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Man kann sich das schrittweise Einlesen der Datei mit Hilfe\n",
+ "eines Schreib-/Lesekopfs vorstellen, der sich\n",
+ "entlang eines Stroms von Bytes bzw. Zeichen bewegt.\n",
+ "Jeder Lesevorgang bewegt den Kopf um die Anzahl der\n",
+ "gelesenen Zeichen vorwärts. Die Position des Kopfs\n",
+ "innerhalb der Datei kann mittels tell
\n",
+ "ermittelt werden."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"091a Textdatei\", \"r\") as f:\n",
+ " block_size = 10\n",
+ " block = f.read(block_size)\n",
+ "\n",
+ " while len(block)>0:\n",
+ " print(block, end='')\n",
+ " pos = f.tell()\n",
+ " print(f\"<{pos}>\", end='')\n",
+ " block = f.read(block_size)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Die Position des Schreib-/Lesekopfs lässt sich auch\n",
+ "durch die Methode seek
verändern."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"091a Textdatei\", \"r\") as f:\n",
+ " f.seek(110)\n",
+ " print(f.read())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Textdateien sind i.d.R. in Zeilen aufgeteilt, die jeweils\n",
+ "mit einem Zeilenendezeichen abschließen. Die Methode\n",
+ "readline
liest aus einer Textdatein\n",
+ "die Daten bis zum nächsten Zeilenende."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"091a Textdatei\", \"r\") as f:\n",
+ " line = f.readline()\n",
+ " while len(line)>0:\n",
+ " print(line, end='')\n",
+ " line = f.readline()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Diese häufig benötigte Variante des Lesens einer Textdatei\n",
+ "kann in Python auch mittels einer for
-Schleife\n",
+ "realisiert werden."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"091a Textdatei\", \"r\") as f:\n",
+ " for line in f:\n",
+ " print(line, end='')"
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Slideshow",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/09kapitel/093 Schreiben.ipynb b/09kapitel/093 Schreiben.ipynb
new file mode 100644
index 0000000..d6340f6
--- /dev/null
+++ b/09kapitel/093 Schreiben.ipynb
@@ -0,0 +1,160 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "# Software Entwicklung \n",
+ "\n",
+ "## Kapitel 9: IO\n",
+ "\n",
+ "### 9.4 Dateien schreiben\n",
+ "\n",
+ "Voraussetzung dafür, dass in eine Datei geschrieben werden kann,\n",
+ "ist ein passender Bearbeitungsmodus."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"093a Textdatei\", \"w\") as f:\n",
+ " f.write(\"Nürnberg ist schön!\")\n",
+ "\n",
+ "with open(\"093a Textdatei\", \"r\") as f:\n",
+ " print(f.read())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Wird der Schreib-/Lesekopf zurückgesetzt, kann der bestehende Inhalt überschrieben werden."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"093a Textdatei\", \"w\") as f:\n",
+ " f.write(\"Nürnberg ist nice!\")\n",
+ " pos = f.tell()-5\n",
+ " f.seek(pos)\n",
+ " f.write(\"schön!\")\n",
+ "\n",
+ "with open(\"093a Textdatei\", \"r\") as f:\n",
+ " print(f.read())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Durch den Bearbeitungsmodus 'r+'
ist es auch\n",
+ "möglich, mit einem Datei-Objekt zu lesen und zu schreiben."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"093a Textdatei\", \"r+\") as f:\n",
+ " f.write(\"Nürnberg ist nice!\")\n",
+ " pos = f.tell()-5\n",
+ " f.seek(pos)\n",
+ " f.write(\"schön!\")\n",
+ " f.seek(0)\n",
+ " print(f.read())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Anders als bei print
wird bei write
\n",
+ "kein Zeilenwechsel angehängt; es muss also ggf. selbst\n",
+ "hinzugefügt werden."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"093a Textdatei\", \"r+\") as f:\n",
+ " f.write(\"Zeile 1\")\n",
+ " f.write(\"Zeile 2\\n\")\n",
+ " f.write(\"Zeile 3\\n\")\n",
+ " f.seek(0)\n",
+ " print(f.readline())"
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Slideshow",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/09kapitel/094 Uebung.ipynb b/09kapitel/094 Uebung.ipynb
new file mode 100644
index 0000000..6082b70
--- /dev/null
+++ b/09kapitel/094 Uebung.ipynb
@@ -0,0 +1,108 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "\n",
+ "Gegeben ist eine binäre Grafikdatei ohm.png
."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from IPython.display import Image\n",
+ "Image(filename=\"ohm.png\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "_Aufgabe 1_\n",
+ "\n",
+ "Erstellen Sie ein Python-Programm, dass diese Datei in eine zweite\n",
+ "Datei logo.png
kopiert. Ãœbertragen Sie die Daten bitte\n",
+ "in 32 Byte Paketen."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from IPython.display import Image\n",
+ "\n",
+ "\n",
+ "with open(\"ohm.png\", \"rb\") as source_file:\n",
+ " with open(\"logo.png\", \"wb\") as dest_file:\n",
+ " buffer_size = 32\n",
+ " buffer = source_file.read(buffer_size)\n",
+ " while len(buffer)>0:\n",
+ " dest_file.write(buffer)\n",
+ " buffer = source_file.read(buffer_size)\n",
+ "\n",
+ "\n",
+ "Image(filename=\"logo.png\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Slideshow",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/09kapitel/095 Sockets.ipynb b/09kapitel/095 Sockets.ipynb
new file mode 100644
index 0000000..5237bec
--- /dev/null
+++ b/09kapitel/095 Sockets.ipynb
@@ -0,0 +1,186 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "# Software Entwicklung \n",
+ "\n",
+ "## Kapitel 9: IO\n",
+ "\n",
+ "### 9.5 Sockets\n",
+ "\n",
+ "So wie der Begriff *Datei* eine griffige Bezeichnung für eine Ansammlung dauerhaft gespeicherter Bytes ist, wird\n",
+ "mit *Socket* ein Endpunkt für Netzwerkverbindungen bezeichnet.\n",
+ "Die Kommunikation findet zwischen zwei Sockets *bidirektional* statt, d.h. über jedes Socket können Bytes versendet\n",
+ "und empfangen werden."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Sockets stehen in Python durch das Standard-Modul sockets
zur Verfügung. Wie schon Dateien belegen auch\n",
+ "Sockets Betriebssystem-Ressourcen, so dass die Verwendung eines Kontextmanagers ratsam ist."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import socket\n",
+ "\n",
+ "with socket.socket() as s:\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "#### 9.5.1 Client-Server-Modell\n",
+ "\n",
+ "Zwar sind bei einer bestehenden Kommunikationsverbindung zwischen zwei Sockets die beiden Sockets prinzipiell\n",
+ "gleichberechtigt, jedoch gibt es Unterschiede beim Kommunikationsaufbau.\n",
+ "\n",
+ "* Ein *Server-Socket* verhält sich passiv, d.h. es wartet auf eingehende Verbindungsanfragen. Trifft eine solche\n",
+ " ein, generiert es ein weiteres Socket, das fest dieser Verbindung zugeordnet wird und über das die\n",
+ " anschließende Kommunikation abgewickelt wird. Das *Server-Socket* selbst verbleibt im Wartezustand.\n",
+ "\n",
+ "* Ein *Client-Socket* übernimmt den aktiven Part. Es kontaktiert das *Server-Socket* und geht selbst die\n",
+ " Verbindung mit dem daraufhin bereitgestellten Socket ein."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Die Verbindungsaufnahme folgt einem vorgegebenen Ablauf aus definierten Schritten (auch: *Kommunikationsprimitive*).\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "#### 9.5.2 Server-Socket\n",
+ "\n",
+ "Ein *Server-Socket* ist eindeutig festgelegt durch das Protokoll (z.B. TCP oder UDP), den Rechner\n",
+ "(z.B. durch dessen IP-Adresse) und eine Portnummer. Letztere dient dazu, dass mehrere Server-Sockets gleichzeitig\n",
+ "auf einem Rechner existieren können, ohne sich gegenseitig zu beeinflussen.\n",
+ "\n",
+ "So gibt es z.B. auf dem Server medinf.efi.th-nuernberg.de
mehrere Server-Sockets,\n",
+ "die permanent angesprochen werden können:\n",
+ "\n",
+ "* Port 22: Nimmt Secure-Shell-Anfragen entgegen\n",
+ "* Port 80: Nimmt HTTP-Anfragen entgegen\n",
+ "* Port 443: Nimmt HTTPS-Anfragen entgegen"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Das *Server-Socket* wird mit bind
an einen bestimmten Port und eine\n",
+ "IP-Adresse (ggf. hat ein Rechner mehrere IP-Adressen) gebunden. Das Binden schlägt fehl, wenn die\n",
+ "Kombination bereits von einem anderen Socket belegt ist.\n",
+ "\n",
+ "Anschließend beginnt das *Server-Socket* mit listen
zu lauschen. Das Primitiv accept
\n",
+ "blockiert den Server, bis ein *Client-Socket* Verbindung aufnimmt. Damit entsteht ein weiteres Socket mit\n",
+ "einer vom System vergebenen Portnummer, das dediziert nur für die Verbindung mit diesem Client verwendet wird.\n",
+ "\n",
+ "Ãœber dieses neue Socket folgen danach Primitive zum Senden\n",
+ "send
und Empfangen receive
von Nachrichten. Anzahl und Abfolge sind\n",
+ "für Sockets nicht festgelegt, müssen aber zwischen Client und Server abgestimmt sein. So legt z.B.\n",
+ "das auf Sockets aufsetzende Protokoll HTTP fest, dass zunächst eine Nachricht vom Client\n",
+ "an den Server (*HTTP-Request*) und anschließend eine Nachricht vom Server an den Client\n",
+ "(*HTTP-Response*) gesendet wird. Während in der ersten Version von HTTP danach die\n",
+ "Verbindung wieder abgebaut wurde, ist es aktuellen Versionen möglich, dieses Abfolge\n",
+ "wiederholt über eine bestehende Verbindung abzuwickeln."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Eine etablierte Verbindung ist mit close
zu schließen. Bei Verwendung eines\n",
+ "Kontextmanagers erfolgt dies automatisch.\n",
+ "\n",
+ "Auch das *Server-Socket* muss geschlossen werden, um das Binden an einen Port zu beenden\n",
+ "und den Port freizugeben."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Slideshow",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/09kapitel/096 Server.ipynb b/09kapitel/096 Server.ipynb
new file mode 100644
index 0000000..d02499d
--- /dev/null
+++ b/09kapitel/096 Server.ipynb
@@ -0,0 +1,102 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "# Software Entwicklung \n",
+ "\n",
+ "## Kapitel 9: IO\n",
+ "\n",
+ "### 9.5 Sockets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "#### 9.5.3 Beispiel für einen Server"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import socket\n",
+ "\n",
+ "with socket.socket() as server: # Generieren eines Sockets\n",
+ " host = socket.gethostname() # Die eigene IP-Nummer\n",
+ " port = 12345 # Festlegen des Ports\n",
+ " server.bind((host, port)) # Bind\n",
+ " server.listen() # Listen (blockiert nicht)\n",
+ " print(\"Ich lausche...\")\n",
+ "\n",
+ " for contact in range(0, 10):\n",
+ " connection, addr = server.accept() # blockiert\n",
+ " with connection:\n",
+ " print('Verbindung von ', addr)\n",
+ " msg = f'Danke für das Vorbeischauen, #{contact+1}!\\n'\n",
+ " connection.send(msg.encode('utf-8')) # Senden \n",
+ "\n",
+ " print(\"Das wars!\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Textnachrichten, die über ein Socket versendet werden sollen, müssen in Bytes\n",
+ "umgewandelt werden.\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Slideshow",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/09kapitel/097 Client.ipynb b/09kapitel/097 Client.ipynb
new file mode 100644
index 0000000..6cadf7f
--- /dev/null
+++ b/09kapitel/097 Client.ipynb
@@ -0,0 +1,146 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "# Software Entwicklung \n",
+ "\n",
+ "## Kapitel 9: IO\n",
+ "\n",
+ "### 9.5 Sockets\n",
+ "\n",
+ "#### 9.5.4 Client-Socket\n",
+ "\n",
+ "Da das *Client-Socket* den Server kontaktiert, benötigt es die Adresse und den\n",
+ "Port des *Server-Sockets*. Mit connect
\n",
+ "wird die Verbindung aufgebaut, sofern an der Gegenstelle ein Server\n",
+ "im Primitiv accept
wartet.\n",
+ "\n",
+ "Danach folgen Primitive zum Senden\n",
+ "send
und Empfangen receive
von Nachrichten\n",
+ "in einer mit dem Server abgestimmten Reihenfolge.\n",
+ "\n",
+ "Abschließend ist die Verbindung mit close
zu schließen. Bei Verwendung eines\n",
+ "Kontextmanagers erfolgt dies automatisch."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "#### 9.5.5 Beispiel für einen Client"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import socket\n",
+ "\n",
+ "with socket.socket() as client: # Generieren des Sockets\n",
+ " host = socket.gethostname() # Server-IP\n",
+ " port = 12345 # Server-Port\n",
+ "\n",
+ " client.connect((host, port)) # Connect\n",
+ " bytes = client.recv(1024) # Receive\n",
+ " print (bytes.decode('utf-8'))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Möchte man ein Socket wie eine Datei behandeln, also statt send
und\n",
+ "recv
lieber write
und read
verwenden, so kann\n",
+ "mit makefile
eine \"Hülle\" um das Socket gelegt werden, die die\n",
+ "(meisten) Funktionen für Dateien anbietet und übersetzt."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import socket\n",
+ "\n",
+ "with socket.socket() as client: # Generieren des Sockets\n",
+ " host = socket.gethostname() # Server-IP\n",
+ " port = 12345 # Server-Port\n",
+ "\n",
+ " client.connect((host, port)) # Connect\n",
+ " with client.makefile(encoding=\"utf-8\") as file:\n",
+ " text = file.readline()\n",
+ " print (text)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ },
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "Textnachrichten, die über ein Socket empfangen werden, müssen aus den übertragenen Bytes\n",
+ "abgeleitet werden. Nutzt man die Dateischnittstelle, findet die Konvertierung dort statt.\n",
+ "\n",
+ " "
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Slideshow",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/09kapitel/ohm.png b/09kapitel/ohm.png
new file mode 100644
index 0000000..bd4d275
Binary files /dev/null and b/09kapitel/ohm.png differ
diff --git a/09kapitel/rise.css b/09kapitel/rise.css
new file mode 100644
index 0000000..c70f5af
--- /dev/null
+++ b/09kapitel/rise.css
@@ -0,0 +1,22 @@
+body.rise-enabled div.inner_cell>div.input_area {
+ font-size: 150%;
+}
+
+body.rise-enabled div.output_subarea.output_text.output_result {
+ font-size: 150%;
+}
+body.rise-enabled div.output_subarea.output_text.output_stream.output_stdout {
+ font-size: 150%;
+}
+
+body.rise-enabled div.output_subarea.output_html.rendered_html.output_result {
+ font-size: 150%;
+}
+
+body.rise-enabled td {
+ font-size: 120%;
+}
+
+body.rise-enabled th {
+ font-size: 120%;
+}
\ No newline at end of file
diff --git a/09kapitel/utf8_text b/09kapitel/utf8_text
new file mode 100644
index 0000000..74b18ae
--- /dev/null
+++ b/09kapitel/utf8_text
@@ -0,0 +1,5 @@
+Zeile 1
+Zeile 2
+Zeile 3
+Zeile 4 Hört das denn nie auf?
+Zeile 5