Strings revisited

Vorbemerkung

In diesem Notebook verwenden wir den Text "Der Mann von vierzig Jahren" von Jakob Wassermann. Dieser Text stammt aus dem Jahr 1913 und wurde vom Projekt Gutenberg digitalisiert (http://www.gutenberg.org/ebooks/15736). Um die Arbeit mit dem Text zu erleichtern, habe ich die Metadaten und Copyright-Hinweise aus dem Text entfernt und in eigene Dateien ausgelagert. Die vollständige Fassung ist unter der oben angeführten Adresse abrufbar. Die Gutenberg-Lizenz erfordert die Angabe des folgendes Textes:

This eBook is for the use of anyone anywhere at no cost and with almost no restrictions whatsoever. You may copy it, give it away or re-use it under the terms of the Project Gutenberg License included with this eBook or online at www.gutenberg.net

Übung: Wir wollen den gesamten Inhalt des Romans ein einen einzigen String mit dem Namen text einlesen. Die einzulesende Datei ist ../data/wassermann/der_mann_von_vierzig_jahren.txt.


In [ ]:
#TODO

str als Sequenztyp

Wie haben bereits den Datentyp str kennengelernt und uns mit Sequenztypen wie list beschäftigt. Da auch str ein Sequenztyp ist, stellten Strings ähnliche Schnittstellen bereit wie andere Sequenztypen. Wir können die Länge des Strings mit der Funktion len() ermitteln:


In [ ]:
# TODO

Mit dem in Operator können wir feststellen, ob ein Zeichen oder ein Substring im String enthalten ist. Gibt es im Text ein Y?


In [ ]:
'Y' in text

Kommt das Wort "Brandung" im Text vor?


In [ ]:
# TODO

Substrings zählen

So wie man mit der count()-Methode zählen kann, wie oft ein Wert in einer Liste oder einem Tupel erscheint, kann auch gezählt werden, wie oft ein Zeichen oder eine Abfolge von Zeichen in einem String vorkommen:


In [ ]:
text.count('Brandung')

Position ermitteln

Manchmal ist es nützlich, festzustellen, an welcher Position ein bestimmtes Zeichen oder ein Teilstring erstmalig vorkommt.


In [ ]:
text.index('Brandung')

Übung: Wie könnten wir ermitteln, wo das zweite Vorkommen des Wortes Brandung beginnt?


In [ ]:

Ähnlich wie index() das erste Vorkommen eines Zeichens oder Teilstrings ermittelt, kann mit rindex() das letzte Vorkommen gefunden werden.

Groß- und Kleinschreibung ändern

Die Methode upper() wandelt alle Zeichen eines Strings in Großbuchstaben um. lower() wandelt alle Zeichen in Kleinbuchstaben um.

Der vermutlich häufigste Einsatzzweck von upper() und lower() liegt darin, unabhängig von Groß- und Kleinschreibung im Text suchen zu können.


In [ ]:
text.count('genau')

In [ ]:
text.count('Genau')

Übung: Überlegen Sie, wie alle Vorkommen von 'Genau' und 'genau' mit einer count()-Anweisung gefunden werden könnten.


In [ ]:

Erzeugen Sie eine Variante von text, die nur Kleinbuchstaben enthält, und die unter dem Namen lc_text verfügbar gemacht wird.


In [ ]:
# TODO

Teilstrings ersetzen

In Python kann ganz einfach jedes Vorkommen eines Teilstrings durch einen anderen Teilstring ersetzt werden. Die entsprechende Methode ist .replace(alt, neu).


In [ ]:
s = 'Mississippi'
s.replace('s', 'b')

Wir können uns das zunutze machen, um im Wassermann-Text alle nicht benötigten Zeilenumbrüche zu entfernen.


In [ ]:
text.replace('\n', ' ')

Allerdings müssen wir hier etwas aufpassen: Absätze werden durch zwei aufeinander folgende Zeilenumbrüche gekennzeichnet. Wenn wir die Absätze erhalten wollen, dürfen wir nur einfache Zeilenumbrüche durch ein Leerzeichen entfernen. Hat jemand eine Lösung dafür?


In [ ]:
# TODO

Strings an bestimmten Zeichen aufsplitten

Die Methode split() trennt einen String an jedem Whitespace und liefert eine Liste von Teilstrings.


In [ ]:
tokens = lc_text.split()
tokens[:20]

Damit haben wir einen primitiven Tokenizer, der unseren Text in einzelne Wörter zerlegt.

Übung: Aus wie vielen Tokens (Wörtern) besteht der Text?


In [ ]:

Übung: Aus wie vielen Types besteht der Text? (Types sind unterschiedliche bzw. distinkte Tokens - man verwirft mehrfach vorkommende Tokens)


In [ ]:

Übung: wie hoch ist die Type-Token-Ratio? (Die Type-Token-Ratio ist ein Wert zwischen 0 und 1, der einein Indikator für den Wortschatz des Autors darstellt.)


In [ ]:

An Nicht-Whitespace splitten

Falls nicht an Whitespace, sondern an einem anderen Zeichen gesplittet werden soll, kann dieses Zeichen (oder eine Zeichenkette) angegeben werden. Wenn wir von der naiven Annahme ausgehen, dass jeder Satz durch einen Punkt begrenzt wird, können wir den Text so in Sätze zerlegen:


In [ ]:
sentences = text.split('.')

Falls nicht an jedem Trennzeichen gesplittet werden soll, sondern z.B. nur beim ersten, kann maxsplit gesetzt werden. Falls wir also nur am ersten Satz interessiert sind:


In [ ]:
text.split('.', 1)

Übung: Splitten Sie den Text absatzweise.


In [ ]:

Vergleichmethoden

Das String-Objekt kennt eine Reihe von Methoden, über die sich Stringvergleiche durchführen lassen. startswith(str) und endswith(str) können dazu verwendet werden um Tokens zu finden, die mit einer bestimmten Zeichenfolge beginnen oder enden. Die Token, die mit der Silbe ab beginnen, finden wir so:


In [ ]:
[token for token in tokens if token.startswith('ab')]

Es wäre schön, wenn hier jedes Wort nur einmal erscheinen würde. Wie das geht, wissen wir ja schon:


In [ ]:

Außerdem wollen wir die Ausgabe alphabetisch sortiert:


In [ ]:

Die Token, die mit der Zeichenkette heit enden, können wir statt mit einer List Comprehension auch in einer Schleife suchen:


In [ ]:
result = []
for token in tokens:
    # TODO
    pass

Zahlen im Text finden wir so:


In [ ]:
[token for token in tokens if token.isdigit()]

Stopwords

In der Datei ../data/stopwords_de.txt haben wir eine Liste mit Stopwords für die deutsche Sprache. Versuchen wir, unsere Tokens um diese Wörter zu bereinigen. Dazu müssen wir zuerst die Stopwort-Liste einlesen:


In [ ]:
with open('../data/stopwords_de.txt') as fp:
    stopwords = [word.rstrip() for word in fp.readlines()]
stopwords

Um nun die Stopwords aus der Liste tokens zu entfernen, können wir eine List Comprension verwenden:


In [ ]:
print(len(tokens)) 
tokens = [token for token in tokens if token not in stopwords]
len(tokens)

Strings bauen mit format()

Um Variablen in Strings einzubauen kennt Python mehrere Möglichkeiten. Die zur Zeit empfohlene ist die Methode format(), die wir schon mehrfach verwendet haben. Die Idee dahinter ist, dass man in einen String Platzhalter einstreut, die mit format() befüllt werden.


In [ ]:
print('Mein Name ist {}. Ich wohne in {}.'.format('Gunter', 'Graz'))

Falls wir einen Wert mehrfach benötigen, können wir die Platzhalter durchnummerieren:


In [ ]:
print('Ihr Name ist {0}. Sie wohnen in {1}. Der Name "{0}" hat {2} Buchstaben'. format('Gunter', 'Graz', len('Gunter')))

Wir können die Platzhalter aber auch benennen:


In [ ]:
print('Ihr Name ist {name}. Sie wohnen in {ort}. Der Name "{name}" hat {laenge} Buchstaben'. format(
    name='Gunter', ort='Graz', laenge=len('Gunter')))

Auf diese Weise ist es relativ einfach, einen Märchengenerator zu schreiben:


In [ ]:
# Wir verwenden eine Zufallsgenerator zur Auswahl der Listenelemente
import random

things = ['Kind', 'Pferd', 'Elch', 'Haus', 'Pirat', 'Gespenst',
          'Buch', 'Spiel', 'Stier', 'Ochse', 'Soldat', 'Bauer']
names = ['Stefan', 'Simon', 'Jakob', 'Moritz', 'Selma', 'Rita',
         'Mama', 'Oma', 'Papa', 'Opa']
places = ['Wald', 'Regenbogen', 'Kanal', 'Fluss', 'Knast',
          'Keller', 'Zoo']


print("""Es war einmal ein {thing1}. Sein Name war {name1}.
{name1} liebte {name2}. {name2} war ein {thing2}.
{name1} und {name2} gingen in den {place1} und erlebten dort
viele Abenteuer.  Und wenn sie nicht gestorben sind, dann
leben sie noch heute.""".format(
    name1=random.choice(names), name2=random.choice(names),
    thing1=random.choice(things), thing2=random.choice(things),
    place1=random.choice(places)))

In [ ]: