{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "35de60ed-1fac-4f0c-a950-901b29e2e143",
   "metadata": {},
   "source": [
    "# Kapitel 8: Zeichenketten und reguläre Ausdrücke\n",
    "[Chapter 8: Strings and Regular Expressions](https://colab.research.google.com/github/AllenDowney/ThinkPython/blob/v3/chapters/chap08.ipynb)\n",
    "\n",
    "Zeichenketten sind anders als ganze Zahlen, Gleitkommazahlen und Boolesche Werte. Eine Zeichenkette ist eine **Folge** (Englisch: *sequence*), d.h. eine geordnete Menge einzelner Werte. In diesem Kapitel lernen wir, wie wir auf die Zeichen zugreifen können, aus denen eine Zeichenkette besteht und lernen einige der Funktionen kennen, die für Zeichenketten bereitgestellt werden.\n",
    "\n",
    "Wir werden außerdem **reguläre Ausdrücke** (Englisch: *regular expressions*) verwenden, die ein mächtiges Hilfsmittel zum Finden von Mustern in Zeichenketten und für Operationen wie Suchen und Ersetzen sind.\n",
    "\n",
    "Als Übung werden Sie die Möglichkeit haben, diese Hilfsmittel für ein Worträtsel namens Wordle zu verwenden.\n",
    "\n",
    "![VIM](https://photos.smugmug.com/Weekly-Comic-About-Programmers/i-fkJRphx/1/35afaf94/L/vim-hires-L.png)\n",
    "\n",
    "[VIM](https://browserling.smugmug.com/Weekly-Comic-About-Programmers/i-fkJRphx/L), comic.browserling.com"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ceb73dff-b1ae-45e5-8329-dda65d93f0e5",
   "metadata": {},
   "source": [
    "## Ihre Lernziele\n",
    "Sie können eine Übersicht der Inhalte dieses Notebooks einblenden mit *Strg + Shift + k*. \n",
    "\n",
    "Beschreiben Sie in 2-3 Stichpunkten kurz was Sie im Seminar heute lernen wollen. Klicken Sie dazu doppelt auf diesen Text und bearbeiten Sie dann den Text:\n",
    "\n",
    "- \n",
    "- \n",
    "- \n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9709a87-b65b-4eb4-847a-695a2ff41650",
   "metadata": {},
   "source": [
    "## Exkurs: Was mir an Python gefällt\n",
    "\n",
    "Text-Statistik ist ganz einfach machbar – mit dem Wissen aus diesem Kapitel können wir z.B. n-Gramme berechnen. Vielleicht kommen Sie ja am Ende dieses Notebooks zu diesem Exkurs zurück. Hier ein Beispiel, welches automatisch eine Webseite herunterlädt und die häufigsten 6-Gramme berechnet. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb9ff8ec-f546-4fb8-a6bd-c8299a93c81f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# klappt nur, wenn das Python-Modul BeautifulSoup installiert ist\n",
    "!pip3 install beautifulsoup4\n",
    "from collections import Counter\n",
    "import requests\n",
    "from bs4 import BeautifulSoup\n",
    "\n",
    "# wir wollen häufige n-Gramme finden - hier n anpassen \n",
    "n = 6\n",
    "\n",
    "# sollte für jede beliebige Webseite funktionieren - hier anpassen\n",
    "link = \"https://de.wikipedia.org/w/index.php?title=Berlin&printable=yes\"\n",
    "\n",
    "# Webseite herunterladen und Text extrahieren\n",
    "text = BeautifulSoup(requests.get(link).text, \"html\").get_text()\n",
    "\n",
    "# n-Gramme zählen\n",
    "ctr = Counter()\n",
    "for i in range(0, len(text) - n + 1):\n",
    "    ngram = text[i:i+n]\n",
    "    # Zeichenketten mit Leerzeichen ignorieren\n",
    "    if ' ' not in ngram:\n",
    "        ctr[ngram] += 1\n",
    "\n",
    "# die Top-10 ausgeben\n",
    "for ngram, frequ in ctr.most_common(10):\n",
    "    print(frequ, ngram, sep='\\t')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b72643b-1146-4846-b66f-e87570747a17",
   "metadata": {},
   "source": [
    "## Herunterladen des unterstützenden Codes\n",
    "Die folgende Zelle lädt eine Datei herunter und führt einen Code aus, der speziell für dieses Notebook verwendet wird. Sie müssen diesen Code nicht verstehen, aber Sie sollten die Zelle vor allen weiteren Zellen in diesem Notebook ausführen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "361d390a",
   "metadata": {
    "id": "361d390a",
    "tags": []
   },
   "outputs": [],
   "source": [
    "from os.path import basename, exists\n",
    "\n",
    "def download(url):\n",
    "    filename = basename(url)\n",
    "    if not exists(filename):\n",
    "        from urllib.request import urlretrieve\n",
    "\n",
    "        local, _ = urlretrieve(url, filename)\n",
    "        print(\"Downloaded \" + str(local))\n",
    "    return filename\n",
    "\n",
    "download('https://github.com/AllenDowney/ThinkPython/raw/v3/thinkpython.py');\n",
    "download('https://github.com/AllenDowney/ThinkPython/raw/v3/diagram.py');\n",
    "\n",
    "import thinkpython"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b9d752a-1280-4335-9cf9-a867177da75c",
   "metadata": {},
   "source": [
    "## Eine Zeichenkette ist eine Folge\n",
    "\n",
    "Eine **Zeichenkette** ist eine **Folge** von Zeichen. Wir können auf die einzelnen Zeichen mit Hilfe des Klammer-Operators zugreifen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dc29d0f9-80e3-4299-9a3a-0b004a7f2a2c",
   "metadata": {},
   "outputs": [],
   "source": [
    "fruit = 'banana'\n",
    "letter = fruit[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c97b955-532e-4dd7-bb48-a7993c8f0aa7",
   "metadata": {},
   "source": [
    "Die zweite Anweisung wählt das Zeichen mit Nummer `1` aus der Zeichenkette `fruit` und weist dieses der Variablen `letter` zu.\n",
    "\n",
    "Der Ausdruck in eckigen Klammern wird **Index** genannt. Der Index gibt an, welches Zeichen der Folge wir haben möchten.\n",
    "\n",
    "Allerdings entspricht das Ergebnis vielleicht nicht ganz dem, was wir erwartet hätten: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60d7ac47-46bf-42a9-ae5a-c5b49c690e01",
   "metadata": {},
   "outputs": [],
   "source": [
    "letter"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "821305dc-d9aa-4f81-9c17-f7d00c225047",
   "metadata": {},
   "source": [
    "Für die meisten Menschen ist der erste Buchstabe von `banana` das `b` und nicht das `a`. Aber in der Informatik wird oft beginnend mit der `0` gezählt und somit hat das erste Zeichen einer Zeichenkette den Index `0`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4c92158b-c163-427c-b410-2f21b85de944",
   "metadata": {},
   "outputs": [],
   "source": [
    "letter = fruit[0]\n",
    "letter"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f83afdb5-c968-4ca3-ab4c-555e6b4a28b6",
   "metadata": {},
   "source": [
    "Also ist `b` der `0.` Buchstabe von `banana`, `a` ist der `1.` Buchstabe und `n` der `2.`.\n",
    "\n",
    "Als Index können wir auch einen Ausdruck verwenden, der Variablen und Operatoren enthält:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d721ab64-a873-49c8-9118-ad582025668a",
   "metadata": {},
   "outputs": [],
   "source": [
    "i = 1\n",
    "fruit[i]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e0c3a608-729a-48db-add1-a201b79ff052",
   "metadata": {},
   "outputs": [],
   "source": [
    "fruit[i + 1]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "159c0633-14e4-4b88-98a5-3ce1fa25be06",
   "metadata": {},
   "source": [
    "Der Wert des Index muss eine ganze Zahl sein. Ansonsten erhalten wir eine Fehlermeldung:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aec20975",
   "metadata": {
    "id": "aec20975",
    "tags": []
   },
   "outputs": [],
   "source": [
    "%%expect TypeError\n",
    "\n",
    "letter = fruit[1.5]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cded76dc-245d-4038-b100-9c789eff1ea1",
   "metadata": {},
   "source": [
    "`len` ist eine eingebaute Funktion, die die Anzahl der Zeichen einer Zeichenkette zurückgibt; Sie haben die `len`-Funktion bereits im 3. Seminar kennengelernt und für die erste Hausaufgabe verwendet:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1ee6a8bb-66ec-4814-901f-41a917efbbf8",
   "metadata": {},
   "outputs": [],
   "source": [
    "fruit = \"banana\"\n",
    "len(fruit)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aabc20b6-c9d1-49a3-ac69-79618de4e4db",
   "metadata": {},
   "source": [
    "Um auf das letzte Zeichen einer Zeichenkette zuzugreifen, würden Sie vielleicht folgendes versuchen: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "618c66de-35f1-4299-8a04-2335317d874b",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%expect IndexError\n",
    "\n",
    "length = len(fruit)\n",
    "last = fruit[length]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "998912f8-1dcb-498f-95f3-d78dd49cddf5",
   "metadata": {},
   "source": [
    "Der Grund für diesen `IndexError` ist, dass es in `banana` kein Zeichen mit dem Index `6` gibt. Da wir ja mit `0` begonnen haben zu zählen, sind die sechs Zeichen mit den Zahlen `0` bis `5` numeriert. Um also das letzte Zeichen zu extrahieren, müssen wir `1` von `length` abziehen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "315614fa-3aeb-4a83-ae10-f2225370b0ad",
   "metadata": {},
   "outputs": [],
   "source": [
    "last = fruit[length - 1]\n",
    "last"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32a8d29d-cd90-434e-88fd-61881f4730cc",
   "metadata": {},
   "source": [
    "Alternativ können wir einen negativen Index nutzen, der rückwärts vom Ende der Zeichenkette aus zählt. Der Ausdruck `fruit[-1]` ergibt das letzte Zeichen, `fruit[-2]` das vorletzte Zeichen, usw.:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c09cad37-deb6-41c7-af8c-88aabc1755f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "fruit[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "58f8c377-cef4-40e2-86e7-701a710535a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "fruit[-2]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ada97470-02f1-4c1c-a13a-7868f22aa926",
   "metadata": {},
   "source": [
    "![Banane](https://upload.wikimedia.org/wikipedia/commons/thumb/8/8a/Banana-Single.jpg/272px-Banana-Single.jpg)\n",
    "\n",
    "([Evan-Amos](https://commons.wikimedia.org/wiki/File:Banana-Single.jpg))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f350819-db43-4252-b0d3-a3458fd04c17",
   "metadata": {},
   "source": [
    "## Zeichenketten-Segmente\n",
    "\n",
    "Ein Teil einer Zeichenkette wird **Segment** (Englisch: *slice*) gennannt. Ein Segment können wir ähnlich wie ein einzelnes Zeichen auswählen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "386b9df2",
   "metadata": {
    "id": "386b9df2"
   },
   "outputs": [],
   "source": [
    "fruit = 'banana'\n",
    "fruit[0:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5cc12531",
   "metadata": {
    "id": "5cc12531"
   },
   "source": [
    "Der Operator `[n:m]` gibt uns den Teil der Zeichenkette vom `n`-ten bis zum `m`-ten Zeichen zurück, einschließlich dem `n`-ten aber ohne das `m`-te Zeichen. Dieses Verhalten ist nicht eingängig, daher hilft es vielleicht, sich vorzustellen, dass die Indizes (Mehrzahl von Index) *zwischen* die Zeichen zeigen, wie in der folgenden Abbildung dargestellt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "05f9743d",
   "metadata": {
    "id": "05f9743d",
    "tags": []
   },
   "outputs": [],
   "source": [
    "from diagram import make_binding, Element, Value\n",
    "\n",
    "binding = make_binding(\"fruit\", ' b a n a n a ')\n",
    "elements = [Element(Value(i), None) for i in range(7)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b09d8356",
   "metadata": {
    "id": "b09d8356",
    "tags": []
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "from diagram import diagram, adjust\n",
    "from matplotlib.transforms import Bbox\n",
    "\n",
    "width, height, x, y = [1.35, 0.54, 0.23, 0.39]\n",
    "\n",
    "ax = diagram(width, height)\n",
    "bbox = binding.draw(ax, x, y)\n",
    "bboxes = [bbox]\n",
    "\n",
    "def draw_elts(x, y, elements):\n",
    "    for elt in elements:\n",
    "        bbox = elt.draw(ax, x, y, draw_value=False)\n",
    "        bboxes.append(bbox)\n",
    "\n",
    "        x1 = (bbox.xmin + bbox.xmax) / 2\n",
    "        y1 = bbox.ymax + 0.02\n",
    "        y2 = y1 + 0.14\n",
    "        handle = plt.plot([x1, x1], [y1, y2], ':', lw=0.5, color='gray')\n",
    "        x += 0.105\n",
    "\n",
    "draw_elts(x + 0.48, y - 0.25, elements)\n",
    "bbox = Bbox.union(bboxes)\n",
    "# adjust(x, y, bbox)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c391abbf",
   "metadata": {
    "id": "c391abbf"
   },
   "source": [
    "Das Segment `[3:6]` wählt zum Beispiel die Buchstaben `ana` an, das bedeutet als Teil eines Segments ist die `6` zulässig, nicht aber als Index.\n",
    "\n",
    "Wenn wir den ersten Index (vor dem Doppelpunkt) weglassen, beginnt das Segment mit dem ersten Zeichen der Zeichenkette:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00592313",
   "metadata": {
    "id": "00592313"
   },
   "outputs": [],
   "source": [
    "fruit[:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1bd7dcb1",
   "metadata": {
    "id": "1bd7dcb1"
   },
   "source": [
    " Wenn wir den zweiten Index weglassen, endet das Segment mit dem letzten Zeichen der Zeichenkette:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "01684797",
   "metadata": {
    "id": "01684797"
   },
   "outputs": [],
   "source": [
    "fruit[3:]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f132959-868c-48c1-94a6-fd5239acb96f",
   "metadata": {},
   "source": [
    "Wenn der erste Index größer oder gleich dem zweiten ist, dann ist das Ergebnis eine **leere Zeichenkette** (Englisch: *empty string*), die durch zwei Anführungszeichen (mit nichts dazwischen) repräsentiert wird:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c7551ded",
   "metadata": {
    "id": "c7551ded"
   },
   "outputs": [],
   "source": [
    "fruit[3:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86b4f7a6-9cce-4ae5-b221-30367e8fcc92",
   "metadata": {},
   "source": [
    "Eine leere Zeichenkette enthält keine Zeichen und hat die Länge `0`. Ansonsten ist sie aber eine ganz normale Zeichenkette.\n",
    "\n",
    "Um unser Beispiel fortzuführen: was meinen Sie, bedeutet `fruit[:]`? Probieren Sie es aus!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5c5ce3e",
   "metadata": {
    "id": "b5c5ce3e",
    "tags": []
   },
   "outputs": [],
   "source": [
    "fruit[:]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87648702-9204-43ec-aeb4-9e0fd8e2beec",
   "metadata": {},
   "source": [
    "## Zeichenketten sind unveränderbar\n",
    "\n",
    "Es ist verlockend, den `[]`-Operator auf der linken Seite einer Zuweisung zu verwenden, um ein Zeichen innerhalb einer Zeichenkette zu verändern. Beispielsweise:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "69ccd380",
   "metadata": {
    "id": "69ccd380",
    "tags": []
   },
   "outputs": [],
   "source": [
    "%%expect TypeError\n",
    "\n",
    "greeting = 'Hello, world!'\n",
    "greeting[0] = 'J'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ebc31f79-55db-4294-a954-ba1109e8df96",
   "metadata": {},
   "source": [
    "Das Ergebnis hierbei ist ein `TypeError`.\n",
    "Das **Objekt** (Englisch: *object*) in diesem Beispiel ist die Zeichenkette und das **Element** (Englisch: *item*) das Zeichen, welches wir zuweisen wollten. Momentan können wir uns unter einem Objekt das gleiche wie einen Wert vorstellen, aber wir werden später (in [Abschnitt 10.10](seminar10.ipynb#Objekte-und-Werte)) genauer kennenlernen, was Objekte sind.\n",
    "\n",
    "Der Grund für den Fehler ist, dass Zeichenketten **unveränderbar** (Englisch: *immutable*) sind. Das heißt, wir können eine existierende Zeichenkette nicht verändern. Das Beste, was wir machen können, ist eine neue Zeichenkette zu erzeugen, die eine Variante des Originals ist:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "280d27a1",
   "metadata": {
    "id": "280d27a1"
   },
   "outputs": [],
   "source": [
    "greeting = 'Hello, world!'\n",
    "new_greeting = 'J' + greeting[1:]\n",
    "new_greeting"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64c3967c-70d6-4767-b5dc-aeeebfb4e6d7",
   "metadata": {},
   "source": [
    "In diesem Beispiel wird ein neuer Anfangsbuchstabe mit einem Segment von `greeting` zusammengefügt. Die ursprüngliche Zeichenkette verändert sich dadurch nicht."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8fa4a4cf",
   "metadata": {
    "id": "8fa4a4cf"
   },
   "outputs": [],
   "source": [
    "greeting"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49e4da57",
   "metadata": {
    "id": "49e4da57"
   },
   "source": [
    "## Zeichenketten vergleichen\n",
    "\n",
    "Die relationalen Operatoren funktionieren mit Zeichenketten. Um zu sehen, ob zwei Zeichenketten gleich sind, können wir den `==`-Operator verwenden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b754d462",
   "metadata": {
    "id": "b754d462"
   },
   "outputs": [],
   "source": [
    "word = 'banana'\n",
    "\n",
    "if word == 'banana':\n",
    "    print('All right, banana.')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee609c93-f1b5-4504-affa-ebca1b59b68d",
   "metadata": {},
   "source": [
    "Andere Vergleichsoperatoren sind nützlich, um Wörter alphabetisch zu ordnen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "44374eb8",
   "metadata": {
    "id": "44374eb8"
   },
   "outputs": [],
   "source": [
    "def compare_word(word):\n",
    "    if word < 'banana':\n",
    "        print(word, 'comes before banana.')\n",
    "    elif word > 'banana':\n",
    "        print(word, 'comes after banana.')\n",
    "    else:\n",
    "        print('All right, banana.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a46f7035",
   "metadata": {
    "id": "a46f7035"
   },
   "outputs": [],
   "source": [
    "compare_word('apple')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b66f449a",
   "metadata": {
    "id": "b66f449a"
   },
   "source": [
    "Python behandelt Groß- und Kleinbuchstaben anders, als wir Menschen es tun würden. Alle Großbuchstaben kommen vor allen Kleinbuchstaben:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a691f9e2",
   "metadata": {
    "id": "a691f9e2"
   },
   "outputs": [],
   "source": [
    "compare_word('Pineapple')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9b916c9",
   "metadata": {
    "id": "f9b916c9"
   },
   "source": [
    "![Coconuts are so far down to the left they couldn't be fit on the chart.  Ever spent half an hour trying to open a coconut with a rock?  Fuck coconuts.](https://imgs.xkcd.com/comics/fuck_grapefruit.png)\n",
    "\n",
    "([Fuck Grapefruit](https://xkcd.com/388/), Randall Munroe)\n",
    "\n",
    "Eine Möglichkeit, dieses Problem zu umgehen, besteht darin, Zeichenketten in ein Standard-Format umzuwandeln - beispielsweise alles in Kleinbuchstaben - und sie erst dann zu vergleichen. \n",
    "\n",
    "Im Deutschen kommt noch hinzu, dass die Umlaute nicht richtig einsortiert werden. Eigentlich würden wir z.B. erwarten, dass `ä` vor `b` einsortiert wird, aber das ist nicht der Fall: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aedf8e65-17cb-4eb7-87e3-5a3b8865bcd4",
   "metadata": {},
   "outputs": [],
   "source": [
    "'älter' < 'bald'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8abe379-aeba-4603-8627-13b943590740",
   "metadata": {},
   "source": [
    "Darauf müssen wir bei Vergleichen achten. (Es gibt die Möglichkeit, durch Verwendung des `locale`-Pakets die Sortierung, z.B. von Listen, zu korrigieren.) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "352bda4c-f472-49e9-8890-88fa5d3baae4",
   "metadata": {},
   "source": [
    "## Methoden für Zeichenketten\n",
    "\n",
    "Für Zeichenketten stellt Python eine Menge nützlicher Methoden bereit. Eine **Methode** (Englisch: *method*) ist ähnlich wie eine Funktion - sie erwartet Argumente und gibt Werte zurück - aber die Syntax ist anders. Vielleicht können Sie erkennen, dass wir für das `turtle`-Modul bereits Methoden verwendet haben. Beispielsweise erwartet die Methode `upper` eine Zeichenkette und gibt eine neue Zeichenkette zurück, in der alle Buchstaben groß geschrieben sind.\n",
    "\n",
    "Anstatt der Funktions-Syntax `upper(word)` nutzt die Methode jedoch die Syntax `word.upper()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa6140a6",
   "metadata": {
    "id": "fa6140a6"
   },
   "outputs": [],
   "source": [
    "word = 'banana'\n",
    "new_word = word.upper()\n",
    "new_word"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bbf16844-d7bd-4270-bd6f-b28ffbba2ee2",
   "metadata": {},
   "source": [
    "In dieser Form der Punkt-Notation wird der *Name der Methode* - `upper` -  und der *Name der Zeichenkette*, auf die die Funktion angewendet werden soll - `word` - angegeben. Die leeren Klammern zeigen, dass diese Methode keine Argumente erwartet.\n",
    "\n",
    "Dies wird **Aufruf** (Englisch: *invocation*) der Methode genannt. In diesem Beispiel würden wir sagen, dass wir `upper` auf `word` aufrufen. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a13d4ef",
   "metadata": {
    "id": "2a13d4ef",
    "tags": []
   },
   "source": [
    "## Dateien schreiben\n",
    "\n",
    "Zeichenketten-Operatoren und Methoden sind hilfreich zum Lesen und Schreiben von Textdateien.\n",
    "Als Beispiel werden wir mit dem Text von *Dracula* arbeiten, einem Roman von Bram Stoker, der über Project Gutenberg verfügbar ist (<https://www.gutenberg.org/ebooks/345>):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3f1dc18",
   "metadata": {
    "id": "e3f1dc18",
    "tags": []
   },
   "outputs": [],
   "source": [
    "download(\"https://www.gutenberg.org/cache/epub/345/pg345.txt\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "963dda79",
   "metadata": {
    "id": "963dda79"
   },
   "source": [
    "Ich habe das Buch als reine Textdatei namens `pg345.txt` heruntergeladen, die wir folgendermaßen zum Lesen öffnen können:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bd2d5175",
   "metadata": {
    "id": "bd2d5175"
   },
   "outputs": [],
   "source": [
    "reader = open('pg345.txt', encoding='utf-8')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b5d99e8c",
   "metadata": {
    "id": "b5d99e8c"
   },
   "source": [
    "Außer dem Text des Buches enthält diese Datei noch einen Abschnitt am Anfang mit Informationen über das Buch und einen Abschnitt am Ende mit Lizenzinformationen.\n",
    "Bevor wir mit dem Text weiterarbeiten, können wir dieses zusätzliche Material entfernen, indem wir spezielle Zeilen am Anfang und Ende des Texts suchen, die mit `'***'` beginnen.\n",
    "\n",
    "Die folgende Funktion nimmt eine Zeile und überprüft, ob diese eine der speziellen Zeilen ist.\n",
    "Sie verwendet die `startswith`-Methode, die überprüft, ob eine Zeichenkette mit einer vorgegebenen Folge von Zeichen beginnt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9c9318c",
   "metadata": {
    "id": "b9c9318c"
   },
   "outputs": [],
   "source": [
    "def is_special_line(line):\n",
    "    return line.startswith('*** ')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2efdfe35",
   "metadata": {
    "id": "2efdfe35"
   },
   "source": [
    "Wir können diese Funktion verwenden, um in einer Schleife durch die Zeilen in der Datei zu gehen und nur die speziellen Zeilen auszugeben:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a9417d4c",
   "metadata": {
    "id": "a9417d4c"
   },
   "outputs": [],
   "source": [
    "for line in reader:\n",
    "    if is_special_line(line):\n",
    "        print(line.strip())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07fb5992",
   "metadata": {
    "id": "07fb5992"
   },
   "source": [
    "Lasst uns nun eine neue Datei namens `pg345_cleaned.txt` erstellen, die nur den Text des Buches enthält.\n",
    "Um erneut in einer Schleife durch das Buch zu gehen, müssen wir es wieder zum Lesen öffnen.\n",
    "Und um eine neue Datei zu schreiben, können wir diese zum Schreiben öffnen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2336825",
   "metadata": {
    "id": "f2336825"
   },
   "outputs": [],
   "source": [
    "reader = open('pg345.txt', encoding='utf-8')\n",
    "writer = open('pg345_cleaned.txt', 'w', encoding='utf-8')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96d881aa",
   "metadata": {
    "id": "96d881aa"
   },
   "source": [
    "`open` nimmt einen optionalen Parameter auf, der einen **\"Modus\"** (Englisch: *mode*) spezifiziert -- in diesem Fall weist `'w'` darauf hin, dass wir die Datei zum Schreiben öffnen.\n",
    "Wenn die Datei noch nicht existiert wird sie erstellt; wenn es sie schon gibt wird der Inhalt ersetzt.\n",
    "\n",
    "Als ersten Schritt arbeiten wir uns in einer Schleife durch die Datei, bis wir die erste spezielle Zeile finden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1b286ee",
   "metadata": {
    "id": "d1b286ee"
   },
   "outputs": [],
   "source": [
    "for line in reader:\n",
    "    if is_special_line(line):\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1989d5a1",
   "metadata": {
    "id": "1989d5a1"
   },
   "source": [
    "Die `break`-Anweisung **\"unterbricht\"** (Englisch: *break*) die Schleife -- das bedeutet, sie beendet die Schleife sofort, bevor das Ende der Datei erreicht ist.\n",
    "\n",
    "Wenn die Schleife verlassen wird enthält `line` die spezielle Zeile, wegen der die Bedingung erfüllt ist:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b4ecf365",
   "metadata": {
    "id": "b4ecf365"
   },
   "outputs": [],
   "source": [
    "line"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f28c3b4",
   "metadata": {
    "id": "9f28c3b4"
   },
   "source": [
    "Weil `reader` den Überblick darüber behält, wo in der Datei wir uns befinden, können wir eine zweite Schleife verwenden, um da weiterzumachen wo wir aufgehört haben.\n",
    "\n",
    "Die folgende Schleife liest den Rest der Datei, Zeile für Zeile.\n",
    "Wenn sie die spezielle Zeile findet, die auf das Ende des Texts hinweist, wird die Schleife unterbrochen.\n",
    "Andernfalls schreibt sie die Zeile in die Output-Datei:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a99dc11c",
   "metadata": {
    "id": "a99dc11c"
   },
   "outputs": [],
   "source": [
    "for line in reader:\n",
    "    if is_special_line(line):\n",
    "        break\n",
    "    writer.write(line)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c07032a4",
   "metadata": {
    "id": "c07032a4"
   },
   "source": [
    "Wenn diese Schleife verlassen wird, enthält `line` die zweite spezielle Zeile:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dfd6b264",
   "metadata": {
    "id": "dfd6b264"
   },
   "outputs": [],
   "source": [
    "line"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c30b41c",
   "metadata": {
    "id": "0c30b41c"
   },
   "source": [
    "Zu diesem Zeitpunkt sind `reader` und `writer` immer noch offen, das heißt wir könnten weiterhin Zeilen aus `reader` lesen oder Zeilen in `writer` schreiben.\n",
    "Um anzuzeigen, dass wir fertig sind, können wir beide Dateien durch das Aufrufen der `close`-Methode schließen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4eda555c",
   "metadata": {
    "id": "4eda555c"
   },
   "outputs": [],
   "source": [
    "reader.close()\n",
    "writer.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5084cdc",
   "metadata": {
    "id": "d5084cdc"
   },
   "source": [
    "Um zu überprüfen, ob dieser Vorgang erfolgreich war, können wir die ersten paar Zeilen der neuen Datei, die wir erstellt haben lesen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5e1e8c74",
   "metadata": {
    "id": "5e1e8c74"
   },
   "outputs": [],
   "source": [
    "for line in open('pg345_cleaned.txt', encoding='utf-8'):\n",
    "    line = line.strip()\n",
    "    if len(line) > 0:\n",
    "        print(line)\n",
    "    if line.endswith('Stoker'):\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34c93df3",
   "metadata": {
    "id": "34c93df3"
   },
   "source": [
    "Die `endswidth`-Methode kontrolliert, ob eine Zeichenkette mit einer vorgegebenen Folge von Zeichen endet."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fcdb4bbf",
   "metadata": {
    "id": "fcdb4bbf"
   },
   "source": [
    "## Suchen und Ersetzen\n",
    "\n",
    "In der isländischen Übersetzung von *Dracula* wurde einer der Charaktere von \"Jonathan\" zu \"Thomas\" umbenannt.\n",
    "Um diese Anpassung in der englischen Version vorzunehmen, können wir in einer Schleife durch das Buch gehen, mit der `replace`-Methode einen Namen durch den anderen ersetzen und das Ergebnis in einer neuen Datei speichern.\n",
    "\n",
    "Wir beginnen damit, die Zeilen der aufgeräumten Version der Datei zu zählen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63ebaafb",
   "metadata": {
    "id": "63ebaafb"
   },
   "outputs": [],
   "source": [
    "total = 0\n",
    "for line in open('pg345_cleaned.txt', encoding='utf-8'):\n",
    "    total += 1\n",
    "\n",
    "total"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ba9b9ca",
   "metadata": {
    "id": "8ba9b9ca"
   },
   "source": [
    "Um zu sehen, ob eine Zeile \"Jonathan\" enthält, können wir den `in`-Operator verwenden, der überprüft ob diese Folge von Zeichen irgendwo in der Zeile auftaucht:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9973e6e8",
   "metadata": {
    "id": "9973e6e8"
   },
   "outputs": [],
   "source": [
    "total = 0\n",
    "for line in open('pg345_cleaned.txt', encoding='utf-8'):\n",
    "    if 'Jonathan' in line:\n",
    "        total += 1\n",
    "\n",
    "total"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27805245",
   "metadata": {
    "id": "27805245"
   },
   "source": [
    "Es gibt insgesamt 199 Zeilen, in denen der Name auftaucht, das ist aber noch nicht die Gesamtzahl, da er ja auch mehr als einmal in einer Zeile vorkommen kann.\n",
    "Um diese Gesamtzahl zu erhalten, können wir die `count`-Methode verwenden, die die Anzahl an Malen, die eine Folge in einer Zeichenkette vorkommt zurückgibt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "02e06ff1",
   "metadata": {
    "id": "02e06ff1"
   },
   "outputs": [],
   "source": [
    "total = 0\n",
    "for line in open('pg345_cleaned.txt', encoding='utf-8'):\n",
    "    total += line.count('Jonathan')\n",
    "\n",
    "total"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68026797",
   "metadata": {
    "id": "68026797"
   },
   "source": [
    "Jetzt können wir `'Jonathan'` folgendermaßen durch `'Thomas'` ersetzen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1450e82c",
   "metadata": {
    "id": "1450e82c"
   },
   "outputs": [],
   "source": [
    "writer = open('pg345_replaced.txt', 'w', encoding='utf-8')\n",
    "\n",
    "for line in open('pg345_cleaned.txt', encoding='utf-8'):\n",
    "    line = line.replace('Jonathan', 'Thomas')\n",
    "    writer.write(line)\n",
    "\n",
    "writer.close()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57ba56f3",
   "metadata": {
    "id": "57ba56f3"
   },
   "source": [
    "Das Ergebnis ist eine neue Datei namens `pg345_replaced.txt`, die eine Verison von *Dracula* enthält, in der Jonathan Harker Thomas heißt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a57b64c6",
   "metadata": {
    "id": "a57b64c6",
    "tags": []
   },
   "outputs": [],
   "source": [
    "total = 0\n",
    "for line in open('pg345_replaced.txt', encoding='utf-8'):\n",
    "    total += line.count('Thomas')\n",
    "\n",
    "total"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c42d2915-e339-4aba-bd11-4c81d6313caf",
   "metadata": {},
   "source": [
    "![Cue letters from anthropology majors complaining that this view of numerolinguistic development perpetuates a widespread myth. They get to write letters like that because when you're not getting a real science degree you have a lot of free time. Zing!](https://imgs.xkcd.com/comics/one_two.png)\n",
    "\n",
    "([One Two](https://xkcd.com/764/), Randall Munroe, [Erklärung des Comics](https://www.explainxkcd.com/wiki/index.php/764:_One_Two) falls Sie mehr lernen wollen)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "cc9af187",
   "metadata": {
    "id": "cc9af187"
   },
   "source": [
    "## Reguläre Ausdrücke\n",
    "\n",
    "Zunächst wollen wir reguläre Ausdrücke unabhängig von Python üben. Arbeiten Sie daher bitte **[diese Einführung](\n",
    "https://regexone.com/lesson/introduction_abcs)** durch. Setzen Sie danach die Arbeit ab hier im Notebook fort.\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2a501f3-f397-4c6a-bcc7-b5056ecbd98d",
   "metadata": {},
   "source": [
    "Wenn wir die genaue Zeichenfolge kennen, nach der wir suchen, können wir den `in`-Operator verwendenden, um sie zu finden und die `replace`-Methode, um sie zu ersetzen.\n",
    "Aber es gibt noch ein weiteres Werkzeug namens **reguläre Ausdrücke** (Englisch: *regular expression*), das diese Operationen ebenfalls durchführen kann -- und noch viele weitere.\n",
    "\n",
    "Um das zu demonstrieren werden wir mit einem einfachen Beispiel beginnen und uns nach oben arbeiten.\n",
    "Nehmen wir einmal erneut an, dass wir alle Zeilen, die ein bestimmtes Wort enthalten finden wollen.\n",
    "Lasst uns zur Abwechslung diesmal nach Referenzen zu dem titelgebenden Charakter des Buches, Graf Dracula (Englisch: *Count Dracula*), suchen.\n",
    "Hier ist eine Zeile, in der er erwähnt wird:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6069027",
   "metadata": {
    "id": "a6069027"
   },
   "outputs": [],
   "source": [
    "text = \"I am Dracula; and I bid you welcome, Mr. Harker, to my house.\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4fd6d11",
   "metadata": {
    "id": "d4fd6d11"
   },
   "source": [
    "Und hier ist das **Muster** (Englisch: *pattern*), das wir zum Suchen verwenden werden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3c19abe",
   "metadata": {
    "id": "e3c19abe"
   },
   "outputs": [],
   "source": [
    "pattern = 'Dracula'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "268f647c",
   "metadata": {
    "id": "268f647c"
   },
   "source": [
    "Ein Modul namens `re` stellt Funktionen zur Verfügung, die mit regulären Ausdrücken zu tun haben.\n",
    "Wir können es folgendermaßen importieren und dann die `search`-Funktion verwenden, um zu überprüfen, ob das Muster im Text auftaucht:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "db588abb",
   "metadata": {
    "id": "db588abb"
   },
   "outputs": [],
   "source": [
    "import re\n",
    "\n",
    "result = re.search(pattern, text)\n",
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e17f6731",
   "metadata": {
    "id": "e17f6731"
   },
   "source": [
    "Wenn das Muster im Text vorkommt, gibt `search` ein `Match`-Objekt zurück, das das Ergebnis der Suche enthält.\n",
    "Neben weiteren Informationen verfügt dieses über eine Variable namens `string`, die den gesuchten Text enthält:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "924524a6",
   "metadata": {
    "id": "924524a6"
   },
   "outputs": [],
   "source": [
    "result.string"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8eab0f6",
   "metadata": {
    "id": "a8eab0f6"
   },
   "source": [
    "Es stellt außerdem eine Methode namens `group` zur Verfügung, die den Teil des Texts zurückgibt, der dem Muster entspricht:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c72b860c",
   "metadata": {
    "id": "c72b860c"
   },
   "outputs": [],
   "source": [
    "result.group()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6962a7d",
   "metadata": {
    "id": "b6962a7d"
   },
   "source": [
    "Zusätzlich gibt es eine Methode namens `span`, die den Index zurückgibt, den Beginn und Ende des Musters im Text haben:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c2f556c",
   "metadata": {
    "id": "7c2f556c"
   },
   "outputs": [],
   "source": [
    "result.span()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f1e5261",
   "metadata": {
    "id": "8f1e5261"
   },
   "source": [
    "Wenn das Muster nicht im Text vorkommt, ist der Rückgabewert von `search` `None`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d5242ef6",
   "metadata": {
    "id": "d5242ef6"
   },
   "outputs": [],
   "source": [
    "result = re.search('Count', text)\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5ed33ff",
   "metadata": {
    "id": "d5ed33ff"
   },
   "source": [
    "Wir können also überprüfen, ob die Suche erfolgreich war, indem wir überprüfen, ob das Ergebnis `None` ist:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18c09b63",
   "metadata": {
    "id": "18c09b63"
   },
   "outputs": [],
   "source": [
    "result == None"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a08e38f6",
   "metadata": {
    "id": "a08e38f6"
   },
   "source": [
    "Wenn wir das nun alles zusammenfügen erhalten wir diese Funktion, die in einer Schleife durch alle Zeilen im Buch geht, bis sie eine Zeile findet, die dem angegebenen Muster entspricht und dann das `Match`-Objekt zurückgibt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fedb7d95",
   "metadata": {
    "id": "fedb7d95"
   },
   "outputs": [],
   "source": [
    "def find_first(pattern):\n",
    "    for line in open('pg345_cleaned.txt', encoding='utf-8'):\n",
    "        result = re.search(pattern, line)\n",
    "        if result != None:\n",
    "            return result"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96570515",
   "metadata": {
    "id": "96570515"
   },
   "source": [
    "Wir können sie verwenden, um die erste Erwähnung eines Charakters zu finden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d7cbe2c2",
   "metadata": {
    "id": "d7cbe2c2"
   },
   "outputs": [],
   "source": [
    "result = find_first('Harker')\n",
    "result.string"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f687fdc",
   "metadata": {
    "id": "3f687fdc"
   },
   "source": [
    "Für dieses Beispiel hätten wir keine regulären Ausdrücke verwenden müssen -- wir hätten das Gleiche einfacher mit dem  `in`-Operator durchführen können.\n",
    "Aber reguläre Ausdrücke können Dinge tun, die der `in`-Operator nicht kann.\n",
    "\n",
    "Wenn das Muster zum Beispiel den senkrechten Strich, `'|'`, enthält, kann es entweder mit der Folge auf der linken oder der auf der rechten Seite übereinstimmen.\n",
    "Nehmen wir an, wir wollen die erste Erwähnung von Mina Murray in dem Buch finden, sind uns aber nicht sicher, ob sie beim Vor- oder Nachnamen genannt wird.\n",
    "Wir können das folgende Muster verwenden, das beide Namen findet:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96c64f83",
   "metadata": {
    "id": "96c64f83"
   },
   "outputs": [],
   "source": [
    "pattern = 'Mina|Murray'\n",
    "result = find_first(pattern)\n",
    "result.string"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8bea66a6",
   "metadata": {
    "id": "8bea66a6"
   },
   "source": [
    "Wir können ein Muster auf diese Weise verwenden um zu sehen, wie oft ein Charakter mit einem der beiden Namen erwähnt wird.\n",
    "Hier ist eine Funktion, die in einer Schleife durch das Buch geht und die Anzahl der Zeilen, die dem angegebenen Muster entsprechen, zählt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0d2e926",
   "metadata": {
    "id": "d0d2e926"
   },
   "outputs": [],
   "source": [
    "def count_matches(pattern):\n",
    "    count = 0\n",
    "    for line in open('pg345_cleaned.txt', encoding='utf-8'):\n",
    "        result = re.search(pattern, line)\n",
    "        if result != None:\n",
    "            count += 1\n",
    "    return count"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e753a5b",
   "metadata": {
    "id": "0e753a5b"
   },
   "source": [
    "Lasst uns nun sehen, wie oft Mina erwähnt wird:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d7e8c5b4",
   "metadata": {
    "id": "d7e8c5b4"
   },
   "outputs": [],
   "source": [
    "count_matches('Mina|Murray')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "780c9fab",
   "metadata": {
    "id": "780c9fab"
   },
   "source": [
    "Das besondere Zeichen `'^'` stimmt mit dem Anfang einer Zeichenkette überein, hiermit können wir also eine Zeile finden, die mit einem bestimmten Muster beginnt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be63c5b0",
   "metadata": {
    "id": "be63c5b0"
   },
   "outputs": [],
   "source": [
    "result = find_first('^Dracula')\n",
    "result.string"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "332bad2e",
   "metadata": {
    "id": "332bad2e"
   },
   "source": [
    "Und das besondere Zeichen `'$'` stimmt mit dem Ende einer Zeichenkette überein, hiermit können wir also eine Zeile finden, die mit einem bestimmten Muster endet (wobei das `newline`-Zeichen ignoriert wird):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37595ac5",
   "metadata": {
    "id": "37595ac5"
   },
   "outputs": [],
   "source": [
    "result = find_first('Harker$')\n",
    "result.string"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4b22b6e",
   "metadata": {
    "id": "d4b22b6e"
   },
   "source": [
    "## Substitution von Zeichenketten\n",
    "(Englisch: *String Substitution*)\n",
    "\n",
    "Bram Stoker wurde in Irland geboren, bei der Veröffentlichung von *Dracula* 1897 lebte er in England.\n",
    "Daher erwarten wir, dass er die britische Schreibweise von Wörtern wie \"centre\" oder \"colour\" verwendet.\n",
    "Um das zu überprüfen können wir das folgende Muster verwenden, das entweder mit \"centre\" oder der amerikanischen Schreibweise, \"center\", übereinstimmt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18237bea",
   "metadata": {
    "id": "18237bea"
   },
   "outputs": [],
   "source": [
    "pattern = 'cent(er|re)'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35abfd7d",
   "metadata": {
    "id": "35abfd7d"
   },
   "source": [
    "In diesem Muster umschließen die Klammern den Teil des Musters, auf den sich der senkrechte Strich bezieht.\n",
    "Dieses Muster stimmt also mit einer Folge überein, die mit `'cent'` beginnt und entweder mit `'er'` oder `'re'` endet:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ce65805f",
   "metadata": {
    "id": "ce65805f"
   },
   "outputs": [],
   "source": [
    "result = find_first(pattern)\n",
    "result.string"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e5703c18",
   "metadata": {
    "id": "e5703c18"
   },
   "source": [
    "Wie erwartet wurde die britische Schreibweise verwendet.\n",
    "\n",
    "Wir können außerdem überprüfen, ob der Autor die britische Schreibweise von \"colour\" benutzt hat.\n",
    "Das folgende Muster enthält das spezielle Zeichen `'?'`, was bedeutet dass das vorhergehende Zeichen optional ist:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af770664",
   "metadata": {
    "id": "af770664"
   },
   "outputs": [],
   "source": [
    "pattern = 'colou?r'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "beed9a7b",
   "metadata": {
    "id": "beed9a7b"
   },
   "source": [
    "Dieses Muster entspricht entweder \"colour\" mit `'u'` oder \"color\" ohne das `'u'`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed67bde7",
   "metadata": {
    "id": "ed67bde7"
   },
   "outputs": [],
   "source": [
    "result = find_first(pattern)\n",
    "line = result.string\n",
    "line"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a31f179",
   "metadata": {
    "id": "1a31f179"
   },
   "source": [
    "Erneut, wie erwartet, wurde die britische Schreibweise verwendet.\n",
    "\n",
    "Nun nehmen wir an, wir wollen eine amerikanische Ausgabe des Buches mit der entsprechenden Schreibweise erstellen.\n",
    "Wir können die `sub`-Funktion im `re`-Modul verwenden, die eine **Zeichenkette ersetzt** (Englisch: *String substitution*):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "52dd938c",
   "metadata": {
    "id": "52dd938c"
   },
   "outputs": [],
   "source": [
    "re.sub(pattern, 'color', line)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04a80fc6",
   "metadata": {
    "id": "04a80fc6"
   },
   "source": [
    "Das erste Argument ist das Muster, das wir finden und ersetzen wollen, das zweite ist womit wir es ersetzen wollen und das dritte ist die Zeichenkette, die wir durchsuchen wollen.\n",
    "Im Ergebnis können wir sehen, dass \"colour\" durch \"color\" ersetzt wurde:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d2e309a2",
   "metadata": {
    "id": "d2e309a2",
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Ich habe diese Funktion verwendet, um nach Zeilen zu suchen, die als Beispiele verwendet werden können\n",
    "\n",
    "def all_matches(pattern):\n",
    "    for line in open('pg345_cleaned.txt', encoding='utf-8'):\n",
    "        result = re.search(pattern, line)\n",
    "        if result:\n",
    "            print(line.strip())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d8b2a9f",
   "metadata": {
    "id": "3d8b2a9f",
    "tags": []
   },
   "outputs": [],
   "source": [
    "# für scrollbaren Output\n",
    "from IPython.display import display, HTML\n",
    "display(HTML(\"\"\"\n",
    "    <style>\n",
    "        .output_scroll {\n",
    "            height: 300px !important;  /* Set your desired height here */\n",
    "            overflow-y: scroll !important;\n",
    "        }\n",
    "    </style>\n",
    "\"\"\"))\n",
    "\n",
    "# Hier ist das Muster, das ich verwendet habe (es nutzt einige Features, die wir uns noch nicht angesehen haben)\n",
    "\n",
    "names = r'(?<!\\.\\s)[A-Z][a-zA-Z]+'\n",
    "\n",
    "all_matches(names)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0318507d",
   "metadata": {
    "id": "0318507d"
   },
   "source": [
    "## Debugging\n",
    "\n",
    "![\"What was the original problem you were trying to fix?\" \"Well, I noticed one of the tools I was using had an inefficiency that was wasting my time.\"](https://imgs.xkcd.com/comics/fixing_problems.png)\n",
    "\n",
    "([Fixing Problems](https://xkcd.com/1739/), Randall Munroe, [Erklärung des Comics](https://www.explainxkcd.com/wiki/index.php/1739:_Fixing_Problems) falls Sie mehr lernen wollen)\n",
    "\n",
    "Beim Lesen und Schreiben von Dateien kann das Debuggen schwierig sein.\n",
    "Wenn wir in einem Jupyter Notebook arbeiten, können wir **Shellbefehle** (Englisch: *shell commands*) zu Hilfe nehmen.\n",
    "\n",
    "**Hinweis: Die Shellbefehle `!head` und `!tail` sind nicht für alle Betriebssysteme verfügbar.\n",
    "Wenn sie bei Ihnen nicht funktionieren, können wir ähnliche Funktionen in Python schreiben.\n",
    "Sehen Sie sich für Vorschläge die Lösungen der Aufgabe 1 in diesem Notebook an.**\n",
    "\n",
    "Um die ersten paar Zeilen einer Datei anzuzeigen, können wir zum Beispiel den Befehl `!head` so verwenden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc39942c",
   "metadata": {
    "id": "cc39942c",
    "tags": []
   },
   "outputs": [],
   "source": [
    "!head pg345_cleaned.txt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d480c02",
   "metadata": {
    "id": "1d480c02"
   },
   "source": [
    "Das Ausrufezeichen,  `'!'`, zu Beginn zeigt an, dass es sich um einen Shellbefehl handelt, der kein Teil von Python ist.\n",
    "Um die letzten paar Zeilen anzuzeigen können wir `!tail` verwenden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc3741e1",
   "metadata": {
    "id": "bc3741e1",
    "tags": []
   },
   "outputs": [],
   "source": [
    "!tail pg345_cleaned.txt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6874023a",
   "metadata": {
    "id": "6874023a"
   },
   "source": [
    "Wenn wir mit großen Dateien arbeiten, kann das Debuggen schwierig sein, da es zuviel Output geben könnte, um ihn von Hand zu kontrollieren.\n",
    "Eine gute Strategie zum Debuggen ist es, mit einem Teil der Datei zu beginnen, das Programm zum Laufen zu bringen und es anschließend mit der vollständigen Datei laufen zu lassen.\n",
    "\n",
    "Um eine kleine Datei, die einen Teil einer größeren enthält zu erstellen, können wir erneut `!head` verwenden, diesmal mit dem Umleitungsoperator, `>`, der angibt, dass das Ergebnis nicht angezeigt, sondern in eine Datei geschrieben werden soll."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d4c92501",
   "metadata": {
    "id": "d4c92501"
   },
   "outputs": [],
   "source": [
    "!head pg345_cleaned.txt > pg345_cleaned_10_lines.txt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3fc851f8",
   "metadata": {
    "id": "3fc851f8"
   },
   "source": [
    "Standardmäßig liest `!head` die ersten 10 Zeilen, es nimmt aber auch ein optionales Argument auf, das die Anzahl der zu lesenden Zeilen angibt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f6606dd",
   "metadata": {
    "id": "8f6606dd"
   },
   "outputs": [],
   "source": [
    "!head -100 pg345_cleaned.txt > pg345_cleaned_100_lines.txt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24871c78",
   "metadata": {
    "id": "24871c78"
   },
   "source": [
    "Dieser Shellbefehl liest die ersten 100 Zeilen von `pg345_cleaned.txt` und schreibt sie in eine Datei namens `pg345_cleaned_100_lines.txt`.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a2a9b97-004b-4291-8404-1b5a4d110353",
   "metadata": {},
   "source": [
    "(Beheben Sie den Fehler und probieren Sie es aus!)\n",
    "\n",
    "![\"What was the original problem you were trying to fix?\" \"Well, I noticed one of the tools I was using had an inefficiency that was wasting my time.\"](https://imgs.xkcd.com/comics/fixing_problems.png)\n",
    "\n",
    "([Fixing Problems](https://xkcd.com/1739/), Randall Munroe, [Erklärung des Comics](https://www.explainxkcd.com/wiki/index.php/1739:_Fixing_Problems) falls Sie mehr lernen wollen)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28beb096-1778-4cad-9136-32ea8ed6d398",
   "metadata": {},
   "source": [
    "## Glossar\n",
    "\n",
    "Legen wir uns eine Liste mit den wichtigsten Begriffen an, die wir im Kapitel 8 gelernt haben:\n",
    "\n",
    "- Folge:\n",
    "- Zeichen:\n",
    "- Index: Der Index beschreibt die Position in einer Folge, an der eine Aktion ausgeführt wird. Das erste Indexelement ist fast immer `0`\n",
    "- Segment:\n",
    "- leere Zeichenkette:\n",
    "- Objekt:\n",
    "- unveränderbar:\n",
    "- Aufruf:\n",
    "- regulärer Ausdruck:\n",
    "- Muster:\n",
    "- Zeichenketten-Substitution:\n",
    "- Shellbefehl:\n",
    "\n",
    "Ergänzen Sie die Liste in eigenen Worten. Das ist eine gute Erinnerungs- und Übungsmöglichkeit."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4306e765",
   "metadata": {
    "id": "4306e765"
   },
   "source": [
    "## Übung"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "66aae3cb",
   "metadata": {
    "tags": [
     "remove-print"
    ]
   },
   "outputs": [],
   "source": [
    "# Diese Zelle weist Jupyter an, detallierte Debugging-Informationen auszugeben, wenn ein Laufzeitfehler\n",
    "# passiert. Lassen Sie sie daher laufen, bevor Sie beginnen an den Aufgaben zu arbeiten.\n",
    "\n",
    "%xmode Verbose\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "772f5c14",
   "metadata": {
    "id": "772f5c14",
    "tags": []
   },
   "outputs": [],
   "source": [
    "download('https://raw.githubusercontent.com/AllenDowney/ThinkPython/v3/words.txt');"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5be97ddc",
   "metadata": {
    "id": "5be97ddc"
   },
   "source": [
    "### Fragen Sie einen virtuellen Assistenten\n",
    "\n",
    "In diesem Kapitel haben wir bisher nur an der Oberfläche dessen gekratzt, was reguläre Ausdrücke alles können.\n",
    "Um sich eine Vorstellung davon machen zu können, was alles möglich ist, fragen Sie einen virtuellen Assistenten: \"Was sind die häufigsten speziellen Zeichen, die in Python in regulären Ausdrücken verwendet werden?\"\n",
    "\n",
    "Sie können außerdem nach einem Muster fragen, das mit bestimmten Arten von Zeichenketten übereinstimmt.\n",
    "Versuchen Sie zum Beispiel zu fragen:\n",
    "\n",
    "* Schreiben einen regulären Ausdruck in Python, der eine zehnstellige Telefonnummer mit Bindestrichen erkennt.\n",
    "\n",
    "* Schreibe einen regularen Ausdruck in Python, der eine Adresse mit Straßennamen, der mit `Str.` oder `Pl.` endet und einer Hausnummer erkennt.\n",
    "\n",
    "* Schreibe einen regulären Ausdruck in Python, der einen vollständigen Namen mit einem geläufigen Titel wie `Frau` oder `Herr` erkennt, gefolgt von einer beliebigen Anzahl von Namen, die mit Großbuchstaben beginnen, wobei einige Namen möglicherweise durch Bindestriche verbunden sind.\n",
    "\n",
    "Wenn Sie etwas noch komplizierteres sehen möchten, versuchen Sie nach einem regulären Ausdruck zu fragen, der jede zulässige URL erkennt.\n",
    "\n",
    "Reguläre Ausdrücke haben oft den Buchstaben `r` vor dem Anführungszeichen, der anzeigt, dass es sich hierbei um eine **\"unformatierte Zeichenkette\"** (Englisch: *raw string*) handelt. Für mehr Informationen darüber fragen Sie einen virtuellen Assistenten: \"Was ist ein raw string in Python?\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8650dec2",
   "metadata": {
    "id": "8650dec2",
    "tags": []
   },
   "outputs": [],
   "source": [
    "from doctest import run_docstring_examples\n",
    "\n",
    "def run_doctests(func):\n",
    "    run_docstring_examples(func, globals(), name=func.__name__)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20dcbbb3",
   "metadata": {
    "id": "20dcbbb3"
   },
   "source": [
    "### Aufgabe 1\n",
    "\n",
    "Versuchen Sie, eine Funktion zu schreiben, die das Gleiche tut wie der Shellbefehl `!head`.\n",
    "Sie sollte als Argumente den Namen einer zu lesenden Datei, die Anzahl der zu lesenden Zeilen und den Namen der Datei, in die die Zeilen geschrieben werden sollen aufnehmen.\n",
    "Wenn der dritte Parameter `None` ist, sollte sie die Zeilen anzeigen, anstatt sie in eine andere Datei zu schreiben.\n",
    "\n",
    "**Hinweis: Eine Möglichkeit zur Lösung dieser Aufgabe besteht in der Verwendung von Listen, welche erst in dem nächsten Notebook behandelt werden. Gerne können Sie daher auch einen virtuellen Assistenten um Hilfe zu fragen. Weisen Sie ihn aber dazu an, keine `with`- oder `try`-Anweisung zu verwenden.**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75b12538",
   "metadata": {
    "id": "75b12538"
   },
   "outputs": [],
   "source": [
    "# hier Lösung hinschreiben"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24f6aac3",
   "metadata": {
    "id": "24f6aac3",
    "tags": []
   },
   "source": [
    "Sie können die folgenden Beispiele verwenden, um Ihre Funktion zu testen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9cbc19cd",
   "metadata": {
    "id": "9cbc19cd",
    "tags": []
   },
   "outputs": [],
   "source": [
    "head('pg345_cleaned.txt', 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19de7df0",
   "metadata": {
    "id": "19de7df0",
    "tags": []
   },
   "outputs": [],
   "source": [
    "head('pg345_cleaned.txt', 100, 'pg345_cleaned_100_lines.txt')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e85ee9b-f313-4366-a8bb-63f0bbe6a15a",
   "metadata": {},
   "source": [
    "\n",
    "<a data-flickr-embed=\"true\"  href=\"https://www.flickr.com/photos/jasoneppink/4964471335\" title=\"Spoiler Alert\"><img src=\"https://farm5.staticflickr.com/4110/4964471335_1f86a923f3_n.jpg\" width=\"320\" height=\"213\" alt=\"Spoiler Alert\"></a><script async src=\"//embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>\n",
    "\n",
    "(Quelle: Jason Eppink, Flickr)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9f54fbb5-b504-412f-b019-0ecd95173138",
   "metadata": {},
   "outputs": [],
   "source": [
    "def head(filename, n=10, filename_out=None):\n",
    "    \"\"\"Ähnliche Funktionalität wie `head` in Linux.\n",
    "    Parameters:\n",
    "        filename (str): Pfad zur Eingabe Textdatei.\n",
    "        n (int): Anzahl der Zeilen.\n",
    "        filename_out (str): Pfad zur Ausgabe Textdatei mit top n Zeilen.\n",
    "    \"\"\"\n",
    "    # Lesen\n",
    "    reader = open(filename, 'r', encoding='utf-8')\n",
    "    head = []\n",
    "    for i, line in enumerate(reader):\n",
    "        if i < n:\n",
    "            print(line)\n",
    "            head.append(line)\n",
    "        else:\n",
    "            break\n",
    "\n",
    "    # Schreiben \n",
    "    if filename_out:\n",
    "        writer = open(filename_out, 'w', encoding='utf-8')\n",
    "        for line in head:\n",
    "            writer.write(line + '\\n')\n",
    "\n",
    "# die ersten 10 Zeilen nur ausgeben\n",
    "head('pg345_cleaned.txt', 10)\n",
    "\n",
    "# die ersten 100 Zeilen Ausgaben und schreiben\n",
    "head('pg345_cleaned.txt', 100, 'pg345_cleaned_100_lines.txt')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "08f350ac-187a-472f-b964-f192dff9f791",
   "metadata": {},
   "outputs": [],
   "source": [
    "def tail(filename, n=10, filename_out=None):\n",
    "    \"\"\"\n",
    "    Ähnliche Funktionalität wie `tail` in Linux.\n",
    "    Parameters:\n",
    "        filename (str): Pfad zur Eingabe Textdatei.\n",
    "        n (int): Anzahl der Zeilen von unten.\n",
    "        filename_out (str): Pfad zur Ausgabe Textdatei mit den letzten n Zeilen.\n",
    "    \"\"\"\n",
    "    # Lesen\n",
    "    reader = open(filename, 'r', encoding='utf-8')\n",
    "    buffer = []\n",
    "    for line in reader:\n",
    "        buffer.append(line)\n",
    "        if len(buffer) > n:\n",
    "            buffer.pop(0)\n",
    "    reader.close()\n",
    "\n",
    "    # Anzeigen\n",
    "    for line in buffer:\n",
    "        print(line, end='')\n",
    "\n",
    "    # Schreiben\n",
    "    if filename_out:\n",
    "        writer = open(filename_out, 'w', encoding='utf-8')\n",
    "        for line in buffer:\n",
    "            writer.write(line)\n",
    "        writer.close()\n",
    "\n",
    "# die letzten 10 Zeilen nur ausgeben\n",
    "tail('pg345_cleaned.txt', 10)\n",
    "\n",
    "# die letzen 100 Zeilen ausgeben und schreiben\n",
    "tail('pg345_cleaned.txt', 100, 'pg345_cleaned_100tail_lines.txt')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1d0f892",
   "metadata": {
    "id": "c1d0f892"
   },
   "source": [
    "### Aufgabe 2\n",
    "\n",
    "*Der Graf von Monte Cristo* ist ein Roman von Alexander Dumas, der als Klassiker angesehen wird.\n",
    "Trotzdem schreibt der Autor Umberto Eco im Vorwort einer englischen Übersetzung des Buches, dass er das Buch für \"einen der am schlechtesten geschriebenen Romane aller Zeiten\" hält.\n",
    "\n",
    "Insbesondere sagt er, es sei \"schamlos in seiner Wiederholung des gleichen Adjektivs“, und erwähnt besonders die Anzahl der Male, \"in denen die Charaktere entweder erschaudern oder blass werden“.\n",
    "\n",
    "Um zu sehen, ob seine Einwände berechtigt sind, lasst uns die Zeilen zählen, die das Wort `pale` (Deutsch: *blass*) in jeglicher Form, zum Beispiel auch `pale`, `pales`, `paled` und `paleness` enthalten, zudem suchen wir auch noch das verwandte Wort `pallor` (Deutsch: *Blässe*).\n",
    "Verwenden Sie einen einzigen regulären Ausdruck, um alle diese Wörter zu finden.\n",
    "Als zusätzliche Herausforderung können Sie darauf achten, dass andere Wörter wie zum Beispiel `impale` (Deutsch: *aufspießen*) nicht gefunden werden -- hierfür könnten Sie einen virtuellen Assistenten um Hilfe fragen."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "742efd98",
   "metadata": {
    "id": "742efd98",
    "tags": []
   },
   "source": [
    "Die folgende Zelle lädt das Buch über Project Gutenberg herunter <https://www.gutenberg.org/ebooks/1184>."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9a74be13",
   "metadata": {
    "id": "9a74be13",
    "tags": []
   },
   "outputs": [],
   "source": [
    "download(\"https://www.gutenberg.org/cache/epub/1184/pg1184.txt\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "881c2f99",
   "metadata": {
    "id": "881c2f99",
    "tags": []
   },
   "source": [
    "Die nächste Zelle lässt eine Funktion laufen, die die Datei von Projekt Gutenberg liest und eine neue Datei schreibt, die nur den Text des Buches ohne weitere Informationen enthält:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "946c63d2",
   "metadata": {
    "id": "946c63d2",
    "tags": []
   },
   "outputs": [],
   "source": [
    "def clean_file(input_file, output_file):\n",
    "    reader = open(input_file, encoding='utf-8')\n",
    "    writer = open(output_file, 'w', encoding='utf-8')\n",
    "\n",
    "    for line in reader:\n",
    "        if is_special_line(line):\n",
    "            break\n",
    "\n",
    "    for line in reader:\n",
    "        if is_special_line(line):\n",
    "            break\n",
    "        writer.write(line)\n",
    "\n",
    "    reader.close()\n",
    "    writer.close()\n",
    "\n",
    "clean_file('pg1184.txt', 'pg1184_cleaned.txt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3eb8f83f",
   "metadata": {
    "id": "3eb8f83f"
   },
   "outputs": [],
   "source": [
    "# hier Lösung hinschreiben"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "29e8c047-055a-4bea-acb8-137860992c29",
   "metadata": {},
   "outputs": [],
   "source": [
    "# hier Lösung hinschreiben"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b6bffe8a",
   "metadata": {
    "id": "b6bffe8a"
   },
   "outputs": [],
   "source": [
    "# hier Lösung hinschreiben"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dfd318a1-49ac-4c0d-b3c0-9e0e592543ed",
   "metadata": {},
   "source": [
    "\n",
    "<a data-flickr-embed=\"true\"  href=\"https://www.flickr.com/photos/jasoneppink/4964471335\" title=\"Spoiler Alert\"><img src=\"https://farm5.staticflickr.com/4110/4964471335_1f86a923f3_n.jpg\" width=\"320\" height=\"213\" alt=\"Spoiler Alert\"></a><script async src=\"//embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>\n",
    "\n",
    "(Quelle: Jason Eppink, Flickr)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d760ee94-cf5d-4bb3-a2f2-fdf14361b37c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import re\n",
    "\n",
    "reader = open('pg1184_cleaned.txt', encoding='utf-8')\n",
    "\n",
    "# Finde pale Variationen, aber keine impale Variationen\n",
    "pattern = r'\\bpal(?:e(?:s|d|ness)?|lor)\\b'\n",
    "\n",
    "# Anzahl\n",
    "count = 0\n",
    "\n",
    "for line in reader:\n",
    "    if re.search(pattern, line):\n",
    "        count += 1\n",
    "\n",
    "print(count)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7db56337",
   "metadata": {
    "id": "7db56337",
    "tags": []
   },
   "source": [
    "Der Zählung nach tauchen diese Wörter in `223` Zeilen des Buches auf, Herr Eco hat also vielleicht Recht."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49b045de-a449-4a9d-9688-d839fd5091d4",
   "metadata": {},
   "source": [
    "### Aufgabe 3\n",
    "\n",
    "Lesen Sie die Dokumentation für die Zeichenketten-Methoden auf [dieser Seite]( https://docs.python.org/3/library/stdtypes.html#string-methods), sie müssen ggf. herunterscrollen bis zum Abschnitt \"4.7.1. String Methods\". Probieren Sie einige der Methoden - mindestens 5 - aus, um sich mit ihnen vertraut zu machen. Die Methoden `strip` und `replace` sind besonders nützlich. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bd9107a9-642e-40d4-a568-fb6a726fe502",
   "metadata": {},
   "outputs": [],
   "source": [
    "# probieren Sie hier einige der Methoden aus\n",
    "\n",
    "# ein Beispiel\n",
    "\"  Manchmal liest man einen Text ein und möchte überflüssige Leerzeichen entfernen.  \".strip()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a47765b5-2af6-4dc8-bbff-9f27f111c45e",
   "metadata": {},
   "source": [
    "Die Dokumentation nutzt eine Syntax, die eventuell verwirrend für Sie ist. Beispielsweise zeigen in `find(sub[, start[, end]])` die eckige Klammern optionale Argumente an. Das bedeutet, dass `sub` benötigt wird, aber `start` optional ist und wenn wir `start` angeben, dann ist `end` optional.\n",
    "\n",
    "![HOW ABOUT A NICE GAME OF STRIP GLOBAL THERMONUCLEAR WAR?](https://imgs.xkcd.com/comics/strip_games.png)\n",
    "\n",
    "([Strip Games](https://xkcd.com/696/), Randall Munroe, [Erklärung des Comics](https://www.explainxkcd.com/wiki/index.php/696:_Strip_Games) falls Sie mehr wissen möchten)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f11e69d6-8050-44de-bdd7-fd8c7026b46e",
   "metadata": {},
   "source": [
    "### Aufgabe 4\n",
    "Einem Zeichenketten-Segment können wir einen dritten Wert übergeben, der die \"Schrittweite\" angibt, d.h. die Anzahl an Schritten zwischen zwei aufeinanderfolgenden Zeichen. Eine Schrittweite von 2 bedeutet, dass jedes zweite Zeichen ausgewählt wird; 3 bedeutet, dass jedes dritte Zeichen ausgewählt wird, etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "84bee5c1-38a2-4d9e-b74b-47e24b21a160",
   "metadata": {},
   "outputs": [],
   "source": [
    "fruit = 'banana'\n",
    "fruit[0:5:2]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70d6e238-c6f8-4d68-9348-fd38ace374b0",
   "metadata": {},
   "source": [
    "Eine Schrittweite von -1 durchläuft das Wort rückwärts, so dass das Segment `[::-1]` eine umgekehrte Zeichenkette erzeugt.\n",
    "\n",
    "Nutzen Sie diese Möglichkeit, um eine einzeilige Variante von `is_palindrome` aus der 7. Aufgabe von [Kapitel 6](seminar06.ipynb) zu schreiben.\n",
    "\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">1. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "\n",
    "Schreiben Sie zunächst den Kopf der Funktion auf. Wir werden die Funktion in mehreren Zeilen entwickeln und dann so zusammenfassen, dass Sie nur eine Zeile brauchen. Schreiben Sie auch schon eine `return`-Anweisung auf und lassen Sie einen Platzhalter zurückgeben.  \n",
    "  </div>       \n",
    "</details>   \n",
    "   \n",
    "  \n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">2. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "\n",
    "Erzeugen Sie eine umgekehrte Zeichenkette des eingegebenen Wortes.\n",
    "  </div>       \n",
    "</details>  \n",
    "\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">3. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "\n",
    "Vergleichen Sie in der `return`- Anweisung die ursprüngliche Zeichenkette mit der umgekehrten Zeichenkette\n",
    "  </div>       \n",
    "</details>\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">4. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "\n",
    "Sie können die umgekehrte Zeichenkette auch in der `return`-Anweisung erzeugen und direkt vergleichen, so erhalten Sie eine einzeilige Funktion\n",
    "    </div>       \n",
    "</details>\n",
    "   \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4bf316b-7e29-4328-9159-fece4be26707",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Implementieren Sie hier die Variante von is_palindrome"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7c31d5f-118a-42b8-aa23-734ff41c8694",
   "metadata": {},
   "source": [
    "\n",
    "<a data-flickr-embed=\"true\"  href=\"https://www.flickr.com/photos/jasoneppink/4964471335\" title=\"Spoiler Alert\"><img src=\"https://farm5.staticflickr.com/4110/4964471335_1f86a923f3_n.jpg\" width=\"320\" height=\"213\" alt=\"Spoiler Alert\"></a><script async src=\"//embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>\n",
    "\n",
    "(Quelle: Jason Eppink, Flickr)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "155bd88d-38f3-4a9e-a6a2-ddcf66bfe8e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def is_palindrome(s):\n",
    "    return s==s[::-1]\n",
    "\n",
    "is_palindrome(\"AnnA\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6dfbb0e4-c9d7-4ae3-9555-c19030db79bd",
   "metadata": {},
   "source": [
    "### Aufgabe 5\n",
    "\n",
    "Die folgenden Funktionen sind eigentlich dafür gedacht, zu prüfen, ob eine Zeichenkette Kleinbuchstaben enthält, aber ein paar der Funktionen sind kaputt. Beschreiben Sie für jede Funktion, was die Funktion tatsächlich tut (unter der Annahme, dass das übergebene Argument eine Zeichenkette ist): "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f5a14890-47db-4ec6-9f15-bac37d27b3be",
   "metadata": {},
   "outputs": [],
   "source": [
    "def any_lowercase1(s):\n",
    "    \"\"\" Fügen Sie hier den Kommentar für diese Funktion ein. \n",
    "    \"\"\"\n",
    "    for c in s:\n",
    "        if c.islower():\n",
    "            return True\n",
    "        else:\n",
    "            return False\n",
    "\n",
    "def any_lowercase2(s):\n",
    "    for c in s:\n",
    "        if 'c'.islower():\n",
    "            return 'True'\n",
    "        else:\n",
    "            return 'False'\n",
    "\n",
    "def any_lowercase3(s):\n",
    "    for c in s:\n",
    "        flag = c.islower()\n",
    "    return flag\n",
    "\n",
    "def any_lowercase4(s):\n",
    "    flag = False\n",
    "    for c in s:\n",
    "        flag = flag or c.islower()\n",
    "    return flag\n",
    "\n",
    "def any_lowercase5(s):\n",
    "    for c in s:\n",
    "        if not c.islower():\n",
    "            return False\n",
    "    return True\n",
    "\n",
    "any_lowercase5 (\"Hallo\")\n",
    "# Testen Sie hier am besten die Funktionen durch und fügen Sie dann \n",
    "# oben zu jeder Funktion einen Kommentar hinzu, der erklärt, was die\n",
    "# jeweilige Funktion wirklich tut."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29047ec3-0cad-4f69-a76c-31ef187a9446",
   "metadata": {},
   "source": [
    "Hier werden Sie keine klassischen Lösungsansätze finden, stattdessen wird für jede Funktion erklärt, was Sie tatsächlich tut. Führen Sie aber auf jeden Fall zunächst jede der Funktionen aus und versuchen Sie in eigenen Worten zu erklären, was die Funktionen machen. Testen Sie immer Großbuchstaben an verschiedenen Stellen im Wort und schauen Sie sich die Ergebnisse an.\n",
    " \n",
    " <details>\n",
    "    <summary type=\"button\" class=\"btn btn-success\">any_lowercase1</summary>\n",
    "  <div class=\"alert alert-success\" role=\"alert\">\n",
    "Diese Funktion prüft ob der erste Buchstabe ein Kleinbuchstabe ist, und gibt darauf basierend entweder `True` oder `False`zurück. Wenn der erste Buchstabe ein Großbuchstabe ist, gibt die Funktion `False` zurück.\n",
    "  </div>       \n",
    "</details> \n",
    " \n",
    " <details>\n",
    "    <summary type=\"button\" class=\"btn btn-success\">any_lowercase2</summary>\n",
    "  <div class=\"alert alert-success\" role=\"alert\">\n",
    "Diese Funktion gibt zunächst nicht einmal einen Booleschen Wert sondern eine Zeichenkette zurück, aber selbst wenn dies die gewollte Ausgabe wäre, macht die Funktion nicht, was in der Aufgabenstellung gefordert ist. Stattdessen wird geprüft, ob die Zeichenkette `c` ein Kleinbuchstabe ist.  </div>       \n",
    "</details> \n",
    " \n",
    " \n",
    " <details>\n",
    "    <summary type=\"button\" class=\"btn btn-success\">any_lowercase3</summary>\n",
    "  <div class=\"alert alert-success\" role=\"alert\">\n",
    "Diese Funktion prüft, ob das letzte Zeichen ein Kleinbuchstabe ist. Dafür verwendet Sie eine sogenannte \"flag\", die den Wahrheitswert jedes einzelnen Buchstaben solange speichert, bis der nächste Buchstabe geprüft wird. Dadurch zählt nur der letzte Buchstabe für die endgültige Ausgabe.  </div>       \n",
    "</details> \n",
    " \n",
    " \n",
    " <details>\n",
    "    <summary type=\"button\" class=\"btn btn-success\">any_lowercase4</summary>\n",
    "  <div class=\"alert alert-success\" role=\"alert\">\n",
    "Diese Funktion tut genau das, was in der Aufgabenstellung verlangt wird. Auch Sie verwendet eine temporäre Variable als \"flag\", um den Wahrheitszustand der Aussage zu speichern, verwendet aber Boolesche Logik, damit `True` zurückgegeben wird, wenn die Funktion mindestens einen Kleinbuchstaben findet. Wir nutzen den Booleschen `or`-Operator, der dann `True` ergibt, wenn mindestens eine der Aussagen wahr ist. Die Flag Variable ist so lange falsch, bis `is_lower()` `True` zurückgibt. Dann ist der Wert der Variable `True` und bleibt es auch, bis die Funktion beendet wird.\n",
    "    </div>       \n",
    "</details> \n",
    " \n",
    " \n",
    " <details>\n",
    "    <summary type=\"button\" class=\"btn btn-success\">any_lowercase5</summary>\n",
    "  <div class=\"alert alert-success\" role=\"alert\">\n",
    "Diese Funktion prüft, ob alle Buchstaben Kleinbuchstaben sind und gibt `False` zurück, sobald sie einen Großbuchstaben findet. Erst wenn kein Buchstabe für `is_lower` `False` zurückgegeben wird, wird `True` zurückgegeben. Schauen Sie sich die `if`-Bedingung genau an und versuchen Sie diese nachzuvollziehen.\n",
    "  </div>       \n",
    "</details> "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c5dfe65c-7eda-4d95-81df-d5cc833b2bf2",
   "metadata": {},
   "source": [
    "### Aufgabe 6\n",
    "\n",
    "Eine [Cäsar-Chiffre](https://de.wikipedia.org/wiki/Caesar-Verschl%C3%BCsselung) ist eine schwache Form der Verschlüsselung, bei der jeder Buchstabe um eine feste Anzahl an Zeichen \"verschoben\" wird. Einen Buchstaben zu verschieben heißt, ihn durch das Alphabet zu schieben, wobei wir, falls notwendig, wieder am Anfang anfangen, sodass `A` um `3` verschoben `D` ergibt und `Z` um `1` verschoben `A`. \n",
    "\n",
    "![Actual actual reality: nobody cares about his secrets.  (Also, I would be hard-pressed to find that wrench for $5.)](https://imgs.xkcd.com/comics/security.png)\n",
    "\n",
    "([Security](https://xkcd.com/538/), Randall Munroe; [Erklärung des Comics](https://www.explainxkcd.com/wiki/index.php/538:_Security) falls Sie mehr wissen wollen.)\n",
    "\n",
    "Um ein Wort zu verschieben, verschieben wir jeden Buchstaben um die gleiche Anzahl. Beispielsweise ist `cheer` verschoben um `7` gleich `jolly` und `melon` verschoben um `-10` ist gleich `cubed`. Im Film [2001: Odyssee im Weltraum](https://de.wikipedia.org/wiki/2001:_Odyssee_im_Weltraum) heisst der Bordcomputer `HAL`, was `IBM` verschoben um `-1` entspricht.\n",
    "\n",
    "![HAL 9000](https://upload.wikimedia.org/wikipedia/commons/f/f6/HAL9000.svg)\n",
    "\n",
    "([Cryteria](https://commons.wikimedia.org/wiki/File:HAL9000.svg))\n",
    "\n",
    "Schreiben Sie eine Funktion `rotate_word`, die eine Zeichenkette und eine ganze Zahl als Argument erwartet und eine neue Zeichenkette zurückgibt, die die Zeichen der ersten Zeichenkette verschoben um den angegebenen Betrag enthält. \n",
    "\n",
    "Sie können die eingebaute Funktion `ord` nutzen, die den Unicode-Wert eines Zeichens zurückgibt, und die Funktion `chr`, die einen Unicode-Wert wieder in ein Zeichen umwandelt. Die Zeichen des Alphabets (außer den Umlauten) sind alphabetisch kodiert, so dass beispielsweise gilt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a5ccef15-da05-442a-962c-b65607455bea",
   "metadata": {},
   "outputs": [],
   "source": [
    "ord('c') > ord('a')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c3dfa1c-1bcb-48b9-8baa-e2713ad520a4",
   "metadata": {},
   "source": [
    "Denn `'c'` ist der 2. Buchstabe des Alphabets. Passen Sie aber auf: die Zahlenwerte für die Großbuchstaben sind anders.\n",
    "\n",
    "\n",
    "Es folgen einige Hinweise zum Lösen der Aufgabe. Versuchen Sie so viel wie möglich alleine mit Ihrem Partner zu lösen, das ist die beste Übung und fragen Sie nach, wenn Sie etwas nicht verstehen.\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">1. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "\n",
    "Wie immer fangen wir damit an, den Kopf der Funktion zu schreiben. Überlegen Sie sich, welche Parameter der Funktion übergeben werden und schreiben Sie auch schon die Rückgabe mit einem Platzhalter.\n",
    "  </div>       \n",
    "</details>   \n",
    "   \n",
    "  \n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">2. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "Um ein Wort um eine bestimmte Anzahl an Buchstaben zu verschieben, ist es am einfachsten, jeden Buchstaben um diese Anzahl zu verschieben und dann das neue Wort zusammenzusetzen. Wir werden dies in eine eigene Funktion auslagern. Schreiben Sie das Gerüst dieser Funktion, also den Kopf, die Parameter und die `return`-Anweisung mit einer Platzhaltervariablen.\n",
    "\n",
    "  </div>       \n",
    "</details>  \n",
    "  \n",
    "Wir beschäftigen uns nun erstmal mit der Verschiebung eines einzelnen Buchstabens. Erst wenn das funktioniert, kehren wir zur Funktion zum Verschieben eines ganzen Wortes zurück. \n",
    "  \n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">3. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "Wir werden die `ord`- und `chr`-Funktionen nutzen um die Buchstaben zu verschieben. Dabei sollen Zeichen, die keine Buchstaben sind, nicht verschoben werden (Konkret sollen also Sonderzeichen wie `?,.,!,:` etc. und Leerzeichen einfach zurückgegeben werden). Wenn ein Zeichen weder groß noch klein geschrieben ist, ist es ein Sonderzeichen. Auch müssen durch die unterschiedlichen Stellenwerte Groß- und Kleinbuchstaben getrennt voneinander behandelt werden. Wie genau wir mit den Buchstaben umgehen, schauen wir uns noch an. Schreiben Sie einen Test, der prüft, ob Sie einen Groß- oder Kleinbuchstaben oder ein Sonderzeichen haben.\n",
    "   </div>       \n",
    "</details>\n",
    "\n",
    "<details> \n",
    "    <summary type=\"button\" class=\"btn btn-primary\">4. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "\n",
    "    Prüfen Sie mit `if`, `elif` und `else` und den Methoden `isupper()` und `islower()`, welche Bedingung wahr ist. Alle Sonderzeichen sollten im `else` Fall behandelt und unverändert zurückgegeben werden. Für die anderen beiden Zeilen können Sie sich einen eindeutigen Platzhalterwert überlegen. Testen Sie nun zunächst einmal, ob Ihre Funktion die verschiedenen Fälle eindeutig und fehlerfrei identifiziert. Machen Sie erst weiter, wenn es funktioniert.\n",
    "  </div>       \n",
    "</details>\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">5. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "     \n",
    "Wenn Sie nur den numerischen Wert des Buchstaben und die Verschiebung addieren und dann wieder in einen Buchstaben verwandeln, funktioniert das in vielen Fällen gut. Probieren Sie aus, was passiert, wenn Sie über z hinaus zurück zum Start des Alphabets gehen wollen. Was passiert? Wir werden uns im nächsten Schritt damit beschäftigen, wie wir das Problem lösen können.\n",
    "      \n",
    "  </div>       \n",
    "</details>\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">6. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "Wir wollen damit beginnen, einen Vergleichswert festzulegen. Wir verwenden dafür den Zahlenwert des Buchstaben a, dabei müssen wir zwischen Groß- und Kleinschreibung unterscheiden. Weisen Sie beide Werte innerhalb der `if`-Schleife derselben Variablen zu. Damit ist der Wert in Abhängigkeit der Schreibweise des eingegebenen Buchstaben festgelegt und wir können im Weiteren die Schreibweise ignorieren.      \n",
    "  </div>       \n",
    "</details>\n",
    "\n",
    "Die nächsten Schritte sind zunächst eventuell etwas verwirrend, machen aber hoffentlich am Ende der Hinweise wieder Sinn. Sollten Sie nicht verstehen, warum etwas passiert, fragen Sie bitte nach!\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">7. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "Zunächst wollen wir mit Hilfe des Vergleichswertes (dem Wert für a) den Index des Buchstaben im Alphabet festlegen. Dabei wird bei 0 mit dem Zählen begonnen und bei 25 für z aufgehört. Es besteht dieselbe Verschiebung in unserer Vorstellung, wie bei Computerindexen üblich. Um die Position zu berechnen, ziehen wir den Wert der Vergleichvariable vom ordinalen Wert des Buchstabens ab. Lassen Sie sich diese Zahl ruhig einmal für ein paar Buchstaben ausgeben und schauen Sie, ob die Werte stimmen.   \n",
    "  </div>       \n",
    "</details>\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">8 Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "      \n",
    "Als nächstes können wir den Index für den verschobenen Buchstaben berechnen, indem wir n zum Index addieren. Nun funktioniert die Funktion bis zu dem Punkt, an dem wir von `z` zu `a` gehen müssen. Wie wir damit umgehen, schauen wir uns im nächsten Schritt an.     \n",
    "    </div>       \n",
    "</details>\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">9. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "      \n",
    "Um den Index zu bekommen, selbst wenn wir über 25 hinaus zurück zu 0 gehen wollen, verwenden wir den modulo Operator, der uns den Rest einer Division zurückgibt. Wird eine Zahl unter 26 mit Rest durch 26 geteilt, ist das Ergebnis `0 Rest diese Zahl`. Wird hingegen zum Beispiel 28 mit Rest durch 26 geteilt, ist das Ergebnis `1 Rest 2`. \n",
    "    </div>       \n",
    "</details>\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">10. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "      \n",
    "Wenn wir nun den Index unseres neuen Buchstabens haben, müssen wir noch den tatsächlichen ordinalen Wert des Buchstabens berechnen. Dafür kehren wir das um, was wir zu Beginn der Berechnung gemacht haben und addieren den Startwert wieder hinzu. \n",
    "   </div>       \n",
    "</details>\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">11. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "      \n",
    "Jetzt müssen wir noch den ordinalen Wert mit der `chr`-Funktion in einen Buchstaben verwandeln und zurückgeben. Testen Sie, dass die Funktion das tut was Sie soll und auch funktioniert, wenn Sie eine Verschiebung über die Grenzen des Alphabets vornehmen. \n",
    "    </div>       \n",
    "</details>\n",
    "\n",
    "Wir können jetzt Buchstaben um einen bestimmten Wert verschieben. Nun wenden wir uns wieder der Funktion zu, die ein ganzes Wort nimmt und entsprechend verschiebt.\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">12. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "\n",
    "Da wir Zeichenketten, nachdem sie erzeugt wurden, nicht mehr verändern können, müssen wir eine neue Zeichenkette erzeugen, die eine verschobene Kopie der alten Zeichenkette ist. Wir wollen mit einer leeren Zeichenkette anfangen und dann nach und nach neue Buchstaben hinten an die Zeichenkette anhängen. Im nächsten Schritt müssen wir auf jeden Buchstaben den verschobenen Buchstaben finden. Überlegen Sie, wie Sie die eingegebene Zeichenkette Buchstabe für Buchstabe durchlaufen können.\n",
    "\n",
    "    </div>       \n",
    "</details>\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">13. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "\n",
    "In der `for`-Schleife fügen Sie den Rückgabewert der `rotate_letter` der neuen Zeichenkette hinzu, indem Sie die Funktion mit dem entsprechenden Buchstaben aufrufen. \n",
    "      \n",
    "    </div>       \n",
    "</details>\n",
    "\n",
    "<details>\n",
    "    <summary type=\"button\" class=\"btn btn-primary\">14. Hinweis</summary>\n",
    "  <div class=\"alert alert-info\" role=\"alert\">\n",
    "\n",
    "Denken Sie daran, die neue Zeichenkette auszugeben und testen Sie ihre Funktion um zu sehen, ob Sie das tut, was Sie soll.\n",
    "      \n",
    "    </div>       \n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c888f1f6-a179-4233-9a63-a4947b706e92",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Implementieren Sie hier die Funktion rotate_word"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03aeb7d7-84f6-495a-b06d-dce95e687b9f",
   "metadata": {},
   "source": [
    "![Rot13](https://upload.wikimedia.org/wikipedia/commons/3/33/ROT13_table_with_example.svg)\n",
    "\n",
    "\n",
    "Potentiell anstößige Witze im Internet sind manchmal mittels ROT13 kodiert, was einer Cäsar-Chiffre mit einer Verschiebung um 13 Zeichen entspricht. Falls Sie sich nicht leicht gekränkt fühlen, finden Sie einige der Witze und dekodieren Sie sie. \n",
    "\n",
    "![Rot13](https://i.imgur.com/E5pBxLd.jpg)\n",
    "\n",
    "([Rot13](https://imgur.com/gallery/T7BD6), vnznfyhg)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "84cdb35d-7a83-4d59-a659-851a9993b10d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def rotate_letter (s,n):\n",
    "    if s.isupper():\n",
    "        start= ord('A')\n",
    "    elif s.islower():\n",
    "        start=ord('a')\n",
    "    else:\n",
    "        return s\n",
    "    letter=ord(s)-start\n",
    "    new=(letter+n)%26+start\n",
    "    return chr(new)\n",
    "\n",
    "def rotate_word(word,n):\n",
    "    neu=''\n",
    "    for letter in word:\n",
    "        neu=neu+rotate_letter(letter,n)\n",
    "    return neu\n",
    "        \n",
    "rotate_word(\"cheer\",7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ff62855-5c17-4062-98fb-a09f53c927c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "rotate_word(\"IBM\",-1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aadee942-b8dd-4679-a331-e6023b569ad6",
   "metadata": {},
   "source": [
    "![Speichern](https://amor.cms.hu-berlin.de/~jaeschkr/teaching/spp/floppy.png) Speichern Sie dieses Notebook, so dass Ihre Änderungen nicht verlorengehen (nicht auf einem Pool-Rechner). Rufen Sie dazu im Menü *File* den Punkt *Download as* → *Notebook* auf und nutzen Sie beispielsweise einen USB-Stick, E-Mail, Google Drive, Dropbox oder Ihre [HU-Box](https://box.hu-berlin.de/).  \n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f98530e-bfe6-4319-9898-50940c840ba4",
   "metadata": {},
   "source": [
    "\n",
    "Herzlichen Glückwunsch! Sie haben das 8. Kapitel geschafft!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5cf853f4-5575-408c-8446-bc993452dc66",
   "metadata": {},
   "source": [
    "<img src=\"https://scm.cms.hu-berlin.de/ibi/python/-/raw/master/img/by-nc-sa.png\" alt=\"CC BY-NC-SA\" style=\"width: 150px;\"/>\n",
    "\n",
    "Der Text dieses Notebooks ist als freies Werk unter der Lizenz [CC BY-NC-SA 4.0 ](https://creativecommons.org/licenses/by-nc-sa/4.0/) verfügbar.\n",
    "Der Code dieses Notebooks ist als freies Werk unter der Lizenz [MIT License](https://mit-license.org/) verfügbar.\n",
    "\n",
    "Es handelt sich um übersetzte und leicht veränderte Notebooks von [Allen B. Downey](https://allendowney.com) aus [Think Python: 3rd Edition](https://allendowney.github.io/ThinkPython/index.html).\n"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "colab": {
   "provenance": []
  },
  "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.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}