{
 "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 <code>encode</code> 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 <code>b</code> 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 <code>decode</code>."
   ]
  },
  {
   "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 <code>open</code>, die ein\n",
    "Dateiobjekt zurückliefert. Die Parameter von <code>open</code> 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 <code>'b'</code>\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 <code>read</code> 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 <code>encoding</code> angegeben werden. Mit <code>encoding=None</code> 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 <code>with</code>."
   ]
  },
  {
   "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",
    "<code>\\_\\_entry\\_\\_</code> und <code>\\_\\_exit\\_\\_</code>.\n",
    "Die Methode <code>\\_\\_entry\\_\\_</code> wird beim Betreten des\n",
    "Kontext aufgerufen, die Methode <code>\\_\\_exit\\_\\_</code> beim\n",
    "Verlassen oder beim Auftreten einer nicht behandelten Exception.\n",
    "\n",
    "Beim Datei-Objekt von Python wird in der\n",
    "<code>\\_\\_exit\\_\\_</code>-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
}
