Praktische Einführung in die Informatik

EinfInfCover

Title: Eine praktische Einführung in die Informatik mit Bash und Python
Autor: Tobias Häberlein
Auflage: 1. Auflage (7. September 2011)
ISBN: 3486704230

Links

 

Errata

  • Seite 17, 3. Absatz, …“tricksen“, denn die viele …. -> das „die“ ist
    zuviel (Danke an Herrn Kemmerich)
  • Seite 19, letzte Zeile „…Ein bestimmtes ein Unix-Kommando…“ -> das
    „ein“ ist zuviel.
  • Seite 32 unten: Das Zeichen
    $

    an jedem Zeilenanfang.

  • Seite 33 oben: indexUmgebungsvariable muss raus. (Danke an Herrn Kaufhold)
  • Seite 36 3. Zeile: erfolglos war. (Danke an Herrn Kaufhold)
  • Seite 38, Aufgabe 2.33 a) punkt am ende des Satzes. (Danke an Herrn Kaufhold)
  • Seite 40:
    egrep [^0-9]

    Liefert alle Zeilen, die nicht mit einer Ziffer enden.
    (Danke Herr Golovski)

  • Seite 44 Erste Zeile Klammer fehlt. (Danke an Herrn Kaufhold)
  • Seite 45 2.6.2, 2 Zeile: „bekommt?“ => „bekommt.“ (Danke an Herrn Kaufhold)
  • Seite 47 Aufgabe 2.50 erste zeile: „das“ streichen. (Danke an Herrn Kaufhold)
  • Seite 53 zweiter Paragraph: „Python eigenet“ => „Python eignet“. (Danke an Herrn Kaufhold)
  • Seite 63 zweite Zeile: „d.h“ => „d.h.“ (Danke an Herrn Kaufhold)
  • Seite 71 vierte Zeile: „sein“ zuviel. (Danke an Herrn Kaufhold)
  • Seite 73: Erstes Listing oben: Statt
    range(10,20)

    muss

    range(1,10)

    stehen.

     

  • Seite 78 Beispiel i) „Zahlen von 1 bis 100“ => „Zahlen von 1 bis 1000“. (Danke an Herrn Kaufhold)
  • Seite 80 unten: „EinigeAufgaben“ => „Einige Aufgaben“. (Danke an Herrn Kaufhold)
  • Seite 91: „Dagegen ist das in Zeile 3 definierte Attribut anzAutos“ => “ …Zeile 2…“. (Danke an Herrn Kaufhold)
  • Seite 95, 4.1 erster Satz: „kann bietet“ => „bietet“. (Danke an Herrn Kaufhold)
  • Seite 101 Beispiel 1\.2: „Matcht des String“ => „Matcht den String“. (Danke an Herrn Kaufhold)
  • Seite 107 „geschachtelt..“ => „geschachtelt“. (Danke an Herrn Kaufhold)
  • Seite 115 erster Paragraph, letzter satz: ein „und“ zuviel. (Danke an Herrn Kaufhold)
  • Seite 116, 5.1.3 „unterschiedlchen“ => „unterschiedlichen“ (Danke an Herrn Kaufhold)
  • Seite 119, dritter Punkt: „DatenbankmanagementsystemEN“ (Danke an Herrn Kaufhold)
  • Seite 131, 5.5.2 zweiter Punkt „Entitten“ => Entitäten … +“wird“ dadurch (Danke an Herrn Kaufhold)
    realisiert, dass eine der beiden Tabellen um den Fremdschlüssel der anderen
    erweitert wird. (Danke an Herrn Kaufhold)
  • S.138 Aufgabe 5.13 einre lock-basierten … => einer lock-basierten… (Danke an Herrn Kaufhold)
  • S.142 Fußnote vergessen [Als Framework bezeichnet man… ] (Danke an Herrn Kaufhold)
  • S.147 6.1 letzte zeile vor 6.1.1. „das“ streichen (Danke an Herrn Kaufhold)
  • S.148 6.1.2 Kommunkationsprotokolle => Kommunikationsprotokolle (Danke an Herrn Kaufhold)
  • S.148 6.1.3! erster satz Kommunkationsprotokolle => Kommunikationsaufgaben (Danke an Herrn Kaufhold)
  • S.150 Abbildung 6.2 Die Internet… => Diese sind durch Router, (Danke an Herrn Kaufhold)
  • S.153 im Listing heisst das objekt s in der Erklärung unten sockobj (Danke an Herrn Kaufhold)
  • S.154 dritter Punkt statt sockobj.accept() s.accept() (Danke an Herrn Kaufhold)
  • S.157 Aufgabe 6.7 punkt am ende des satzes
  • S.159 Aufgabe 6.8 punkt am ende des satzes (Danke an Herrn Kaufhold)
  • S.161 Aufgabe 6.11 erstelle => erstellte (Danke an Herrn Kaufhold)
  • S.169 „das Freigabe eines Locks“ => „die Freigabe eines Locks“ (Danke an Herrn Kaufhold)
  • S.171 7.2.3 der zweiten satz im zweiten paragraphen sollte umformuliert werden (Danke an Herrn Kaufhold)
  • S.174 7.3.1 erster para letzte zeile DatenbankmanagementsystemE (Danke an Herrn Kaufhold)
  • Seite 180: Listing 7.5, Erste Zeile muss raus.
  • Seite 180: Listing 7.5, Zeile 3 muss heißen:
    from threading import Thread,Lock
  • S.182 para über Aufgabe 7.7 Schliefe => Schleife (Danke an Herrn Kaufhold)
  • S.182 para über Aufgabe 7.7 Ziele => Zeile (Danke an Herrn Kaufhold)
  • S.183 Definition von „embarrassingly parallel“ doppelt auf Seite 183 und in
    der Fußnote auf Seite 186 (Danke an Herrn Kaufhold)
  • S.185 7.5.2 über Beispiel 1 punkt nach Satzende (Danke an Herrn Kaufhold)
  • S.187 zweite zeile gemeinsamen. Variablen => gemeinsamen Variablen (Danke an Herrn Kaufhold)
  • S.188 Aufgabe 7.10 symmetischen Multiprozessorsystem => symmetRischen
    Multiprozessorsystem (Danke an Herrn Kaufhold)
  • S.189 Beispiel 2 zweite Zeile FließbandverARbeitung (Danke an Herrn Kaufhold)
  • S.192 7.5.3 jeder dieseR Supersteps (Danke an Herrn Kaufhold)
  • S.193 letzte Zeile „macht erzeugt“ => „erzeugt“ (Danke an Herrn Kaufhold)
  • S.194 erste zeile „Der jeder Prozessor“ => „Jeder Prozessor“ (Danke an Herrn Kaufhold)
  • S.194 der beteiligten ProzessE (Danke an Herrn Kaufhold)
  • S.194 „jedoch .“ => „jedoch.“ Space zuviel (Danke an Herrn Kaufhold)

Lösungen zu den Übungsaufgaben

Aufgabe 2.29

Geben Sie eine Unix-Kommandozeile an, die …

  • die Namen aller C-Dateien (Endung: .c) im aktuellen Verzeichnis ausgibt, falls
    welche existieren.
  • den String Es gibt keine C-Dateien ausgibt, falls keine C-Dateien im aktuellen
    Verzeichnis existieren.

Lösung

1. Teil der Aufgabe:

 
ls -l *.c >/dev/null 2>/dev/null && ls *.c
   

2. Teil der Aufgabe:

 
ls -l *.c >/dev/null 2>/dev/null || echo "Es gibt keine C-Dateien"
   

_________________________________________________________________________

Aufgabe 2.38

Geben Sie einen regulären Ausdruck an, mit dessen Hilfe das egrep-Kommando alle Zeilen
ausgibt, die …

(a)

…mit a oder mit b anfangen.

(b)

…nur Ziffern enthalten.

(c)

…genau zwei Worte enthalten.

Lösung

(a)

egrep ^[ab]

(b)

egrep ^[0-9]*$

(c)

egrep ^[ ]*[^ ][^ ]*[ ]*[^ ][^ ]*[ ]*$

_____________________________________________________________________________________Aufgabe 2.39

Verwenden Sie das egrep-Kommando, um …

(a)

… alle Dateien im aktuellen Verzeichnis aufzulisten, die keine Verzeichnisse sind.

(b)

… alle Dateien im aktuellen Verzeichnis aufzulisten, auf denen alle Schreibrechte
haben.

(c)

… alle angemeldeten User aufzulisten, deren Username mit a endet.

Lösung

(a)

ls l | egrep ^[^d]

(b)

ls l | egrep ^..w..w..w

_____________________________________________________________________________________Aufgabe 2.40

Geben Sie ein egrep-Kommando an, mit dem sie alle Zeilen finden, …

(a)

…in denen irgendwo blubber vorkommt.

(b)

…die das Wort blubber enthalten.

(c)

…die mit einer Ziffer anfangen und mit einem Buchstaben aufhören.

(d)

…die genau zweimal den Buchstaben a enthalten.

(e)

…die genau zwei Ziffern enthalten oder genau einmal a enthalten.

Lösung

(a)

egrep blubber

(b)

egrep ( blubber )|( blubber$)|(^blubber )

(c)

egrep ^[0-9].*[azAZ]$

(d)

egrep ^[^a]*a[^a]*a[^a]*$

(e)

egrep (^[^0-9]*[0-9][^0-9]*[0-9][^0-9]*$)|(^[^a]*a[^a]*a[^a]*$)

_____________________________________________________________________________________

Aufgabe 3.16

Wie können Sie in folgendem Ausdruck (der eine verschachtelte Liste darstellt)

[[x],[[[y]]]]

auf den Wert von y zugreifen?

Lösung

   

_____________________________________________________________________________________

Aufgabe 3.22

(a)

Schreiben Sie eine Pythonfunktion teiler(n), die die Liste aller Teiler einer als Parameter
übergebenen Zahl n zurückliefert. Tipp: Am leichtesten mit Verwendung einer
Listenkomprehension. Beispielanwendung:

 
>>>teiler(45) 
>>>[1, 3, 5, 9, 15]
    
(b)

Geben Sie – mit Verwendung der eben geschriebenen Funktion teiler – einen
Python-Ausdruck (kein Kommando!) an, der eine Liste aller Zahlen zwischen 1 und 1000
ermittelt, die genau 5 Teiler besitzen.

(c)

Geben Sie – mit Verwendung der eben geschriebenen Funktion teiler – einen
Python-Ausdruck an, der die Zahl zwischen 1 und 1000 ermittelt, die die meisten Teiler
besitzt.

Lösung

(a)

Eine Funktion die die Liste aller teiler einer Zahl n zurückliefert.

 
def teiler(n): 
 return [i for i in range(1,nif n%i==0]
    
(b)

[i for i in range(1,1000) if len(teiler(i))==5]

(c)

max([(len(teiler(i)), ifor i in range(1,1000) ])[1]

_____________________________________________________________________________________

Aufgabe 3.23

Verwenden Sie eine Listenkomprehension, um eine Liste zu erzeugen, die alle Methoden- und
Attributnamen der Klasse str enthält, die …

(a)

…mit a beginnen.

(b)

…mit er enden.

(c)

…mehr als 10 Zeichen enthalten.

(d)

…den String cod als Teilstring enthalten.

Tipp: Mit dir(str) erhalten Sie die Liste aller Methoden- und Attributnamen der Klasse str; für
obige Aufgaben benötigen Sie evtl. die String-Methoden startswith, endswith, die Funktion
len, und die Operation in.

Lösung

(a)

[s for s in dir(strif s.startswith(a)]

(b)

[s for s in dir(strif s.endswith(er)]

(c)

[s for s in dir(strif len(s)>10]

(d)

[s for s in dir(strif codin s]

_____________________________________________________________________________________

Aufgabe 3.24

Verwenden Sie eine Listenkomprehension, um …

(a)

…eine Liste der Längen aller Methoden- und Attributnamen der Klasse str erzeugt.

(b)

…die Länge des längsten Methoden- oder Attributnamen der Klasse str zu berechnen.

(c)

…den Namen des längsten Methoden- oder Attributnamen der Klasse str zurückliefert.

Lösung

(a)

[len(sfor s in dir(str)]

(b)

max([len(sfor s in dir(str)])

(c)

max([(len(s),sfor s in dir(str)])[1]
Dies entspricht dem sog. Decor-Undecor-Muster. Zunächst werden die Informationen
in dir(str) mit den Werten “dekoriert”, nach denen sortiert werden soll – in diesem
Fall sind das die Längen der Methodennamen. Nach dem Sortieren bzw. nach
der Maximumsbildung wird wieder weg-“dekoriert”, d. h. der Längenwert wird
verworfen und nur der entsprechende Methodenname zurückgeliefert. In obigem
Beispiel geschieht dies mittels der Indizierung [1].

_____________________________________________________________________________________

Aufgabe 3.26


Programmieren Sie mit Hilfe einer Listenkomprehension eine Funktion prims(n), die die Liste
aller Primzahlen bis n zurückliefert.
(Wohlgemerkt: „…zurückliefert …“, nicht „…ausgibt …“. Eine Funktion, die Ergebnisse auf dem
Bildschirm ausgibt, ist im Allgemeinen weniger nützlich. Eine Funktion, die Ergebnisse mit
Hilfe von return zurückliefert, kann man viel einfacher mit anderen Funktionen kombinieren.)

Lösung

Die Liste aller Primzahlen kleiner n unter Verwendung einer Listenkomprehension:

 
>>>from math import sqrt 
>>>def prims(n): 
...  return [i for i in range(2,n+1) if [j for j in range(2,1+int(sqrt(i))) if i%j==0]==[]] 
>>>prims(100) 
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
   

__________________________________________________________________________________________________________________

Aufgabe 3.27


(a)

Geben Sie eine Listenkomprehension an, mit der Sie die Liste aller Quersummen der
Zahlen von 1 bis 1000 ausgeben können.

(b)

Berechnen Sie die Liste aller perfekten Zahlen zwischen 1 und 10000 mit einer
Python-Listenkomprehension.
Ein Zahl heißt perfekt, wenn Sie gleich der Summe ihrer positiven echten Teiler (d. h.
auch sich selbst) ist. Beispielsweise ist 6 eine perfekte Zahl, denn 6=1+2+3.

Lösung

(a)

Die folgende Listenkomprehension gibt die Quersummen der Zahlen von 1 bis 1000 an.
Man beachte, dass dies eine geschachtelte Listenkomprehension ist.

 
[sum([ord(s)-48 for s in str(zahl)]) for zahl in range(1,1001)]
    
(b)

Die Liste aller perfekten Zahlen zwischen 1 und 10000 – wiederum durch eine geschachtelte
Listenkomprehension:

 
>#x003E;>>[i for i in range(1,10001) if sum([j for j in range(1,1+i/2) if i%j==0])==i[6, 28, 496, 8128]
    

_____________________________________________________________________________________

Aufgabe 3.28


Wir wollen Zufallsexperimente mit einem 6-seitigen Würfel durchführen. Sie können hierzu die
Funktion randint aus der Bibliothek random verwenden.

(a)

Erzeugen Sie mittels einer Listenkomprehension eine Liste mit 10 zufälligen Werten
zwischen 1 und 6 – um einen 10-maligen. Wurf mit einem 6-seitigen Würfel zu
simulieren.

(b)

Wie groß ist die Wahrscheinlichkeit, dass bei einem 10-maligen Wurf keine 1 fällt?
Zählen Sie – zur Beantwortung dieser Frage – wieviele 10er-Würfen von (sagen
wir) 100000 10er-Würfen keine 1 enthalten; realisieren Sie dies durch eine
Listenkomprehension. (Sie erhalten eine Näherung der Wahrscheinlichkeit, wenn Sie
die erhaltene Anzahl durch 100000 teilen.

(c)

Wie groß ist die Wahrscheinlichkeit, dass bei einem 10-maligen Wurf genau drei mal
eine 1 fällt?

Lösung

(a)

Folgendermaßen kann man einen 10er-Wurf modellieren:

 
def wurf10(): return [random.randint(1,6) for i in range(10)]
    
(b)

Folgendermaßen kann man zählen, wie oft keine 1 fällt:

 
>>>len([0 for i in range(100000) if 1 not in wurf10()]) 
16254
    
(c)

…und folgendermaßen kann man zählen, wie oft genau drei mal eine 1 fällt:

 
>>>len([0 for i in range(100000) if wurf10().count(1) == 3]) 
15483
    

_____________________________________________________________________________________

Aufgabe 3.29


Verwenden Sie die map-Funktion, um einer (String-)Liste von Zeilen Zeilennummern
hinzuzufügen. Der Ausdruck


[Erste ZeileZweite ZeileUnd die dritte Zeile]


sollte also umgewandelt werden in folgenden Ausdruck:


[1. Erste Zeile2. Zweite Zeile3. Und die dritte Zeile]

Lösung

 
>>>def append(x,y): return str(x+'. ' +y 
>>>map(appendrange(1,4), ['Erste Zeile'...                   'Zweite Zeile'...                   'Und die dritte Zeile']) 
['1. Erste Zeile''2. Zweite Zeile''3. Und die dritte Zeile']
   

_____________________________________________________________________________________

Aufgabe 3.31


Wir wollen alle möglichen Wörter mit bestimmten Eigenschaften erzeugen. Gegeben seien die
folgenden Definitionen:

 
C = 'abcdefghijklmnopqrstuvwxyz' 
D = map(strrange(0,10)) ; A = C+D
   

(a)

Schreiben Sie eine Pythonfunktion woerter(n), die alle Wörter der Länge n mit
Buchstaben aus A erzeugt.
Tipp: Zur Lösung kommt man elegant rekursiv, indem man überlegt, was welchen
Wert woerter(0) und woerter(1) haben muss und anschließend überlegt, wie man
woerter(n) aus woerter(n1) berechnen kann.

(b)

Schreiben Sie eine Pythonfunktion w1d(n), die alle Wörter der Länge n mit
Buchstaben aus A erzeugt, die mindestens eine Ziffer enthalten.

(c)

Schreiben Sie eine Pythonfunktion wdE(n), die alle Wörter der Länge n mit
Buchstaben aus A erzeugt, die mit einer Ziffer enden.

Tipp: c.isdigit() testet, ob das Zeichen c eine Ziffer ist.

Lösung

(a)

Alle Wörter der Länge n mit Buchstaben aus A:

 
def woerter(n): 
 if n==0: return [] 
 if n==1: return list(A return [wort+c for c in A for wort in woerter(n-1)]
    

Wobei man den Fall für n==1 eigentlich nicht braucht; wurde nur der Anschaulichkeit
halber hier eingefügt.

(b)

Alle Wörter der Länge n, die mindestens eine Ziffer enthalten:

 
def w1d(n): 
 def mZ(s): return sum([1 if c.isdigit() else 0 for c in s]) > 0 
 return filter(mZwoerter(n))
    
(c)

Alle Wörter der Länge n mit einer Ziffer am Ende erhält man folgendermäßen:

 
def wdE(n): 
 return filter(lambda ss[-1].isdigit(), woerter(n))
    

_____________________________________________________________________________________

Aufgabe 3.32


Schreiben Sie eine Funktion c2h(c), die das Zeichen c in einen int-Wert umwandelt, falls c eine
hexadezimale Ziffer repräsentiert, d. h. falls c 0123456789abcdefABCDEF. Es soll
beispielsweise gelten:

 
>>>c2h('5'>>>c2h('e'14
   

Lösung

Die Funktion c2h wandelt ein Zeichen, das eine hexadezimale Ziffer repräsentiert, in einen
Integer-Wert um.

 
def c2h(c): 
 if c.isdigit(): return int(c elif c in 'abcdef'return ord(c)-87 
 elif c in 'ABCDEF'return ord(c)-55 
 elsereturn 0
   

__________________________________________________________________________________________________________________

Aufgabe 3.33


Verwenden Sie das mittels reduce-Funktion implementierte Horner-Schema, um eine als
String repräsentierte Binärzahl in die entsprechende Dezimalzahl umzuwandeln.

Lösung

Umwandlung einer als String repräsentierten Binärzahl in die entsprechende Dezimalzahl.

 
>>>binStr = '10011101' 
>>>def f(x,y): return 2*x +y 
>>>reduce(f, [0 if c=='0' else 1 for c in binStr]) 
157
   

__________________________________________________________________________________________________________________

Aufgabe 3.34


Verwenden Sie die reduce-Funktion, um eine Funktion prod(lst) zu definieren, die alle in lst
befindlichen Zahlen aufmultipliziert.

Lösung

 
>>>def f(x,y): return x*y 
>>>reduce(f,

liste

)
   

_____________________________________________________________________________________

Aufgabe 3.35


Verwenden Sie die reduce-Funktion, um eine Funktion max(lst) zu definieren, die das maximale
in lst befindliche Element zurückliefert.

Lösung

 
>>>def f(x,y): 
...  if x<yreturn y 
...  else return x 
>>>reduce(f,

liste

)
   

_____________________________________________________________________________________

Aufgabe 3.36


Verwenden Sie die reduce-Funktion, um eine Liste von Tupeln „flachzuklopfen“ und in eine
einfache Liste umzuwandeln. Beispiel: Die Liste [(1,10), (a,b), ([1], [2])] sollte etwa in die
Liste [1,10,a,b,[1],[2]] umgewandelt werden.

Lösung

 
>>>tuplelist= [(1,10),('a','b' ),([1],[2])]  
>>>def f(x,y): return x+y 
>>>reduce(f,map(list,tuplelist)) 
[1, 10, 'a''b', [1], [2]]
   

Anmerkung: Die Funktion list wandelt beliebige Sequenzen in Listen um.

__________________________________________________________________________________________________________________

Aufgabe 3.46


Verwenden Sie eine Listenkomprehension, um alle Zeilen der Datei test.txt auszugeben, die

(a)

…mit der Zeichenkette Py beginnen.

(b)

…die die Zeichenkette Python enthalten.

(c)

…deren letztes Zeichen ein Kleinbuchstabe ist.

Lösung

(a)

Alle Zeilen aus test.txt, die mit Py beginnen:
[zeile for zeile in open(test.txtif zeile.startswith(Py)]

(b)

Alle Zeilen aus test.txt, die Python enthalten.
[zeile for zeile in open(test.txtif Pythonin zeile]

(c)

Alle Zeilen aus test.txt, deren letztes Zeichen ein Kleinbuchstabe ist.
[zeile for zeile in open(test.txtif zeile[1].islower()]

_____________________________________________________________________________________

Aufgabe 3.47


Berechnen Sie unter Verwendung einer Listenkomprehension …

(a)

…die Summe aller Längen von Zeilen, die mit a beginnen.

(b)

…die Länge der kürzesten Zeile, die keine Leerzeichen enthält.

Lösung

(a)

Summe aller Zeilenlängen von Zeilen, die mit a beginnen.

 
sum([len(zeilefor zeile in open('test.txt'if zeile.startswith('a')])
    
(b)

Die Länge der kürzesten Zeile, die keine Leerzeichen enthält.

 
min([len(zeilefor zeile in open('test.txt'if ' ' not in zeile])
    

_____________________________________________________________________________________

Aufgabe 3.48


Verwenden Sie eine Listenkomprehension, um diejenige Zeile der Datei ’test.txt’
auszugeben, die …

(a)

…am häufigsten das Zeichen a enthält.

(b)

…die meisten Wörter enthält.

(c)

…das längste Wort enthält.

Lösung

(a)

Zeile, die am häufigsten das Zeichen a enthält.

 
max([(zeile.count('a'), zeilefor zeile in open('test.txt')])[1]
    
(b)

Zeile, die die meisten Wörter enthält.

 
max([(len(zeile.split(' ')),zeilefor zeile in open('test.txt')])[1]
    
(c)

Zeile, die das längste Wort enthält.

 
max([ (max(map(len,zeile.split(' '))), zeilefor zeile in open('test.txt')])[1]
    

_____________________________________________________________________________________

Aufgabe 4.1


Geben Sie eine Python-Kommandozeile an, die die Summe aller von einem Leerzeichen
gefolgten Zahlen (d. h. Ziffernfolgen) eines Strings zurückliefert.

Lösung

Die Summe aller von einem Leerzeichen gefolgten Zahlen (d. h. Ziffernfolgen) eines Strings
kann folgendermaßen bestimmt werden:

 
sum([int(z[:-1]) for z in re.findall('[0-9]+ ',string)])
   

Die Variable z in der Listenkomprehension läuft über alle Matches, d. h. über alle in string
enthaltenen Zahlen inkl. des folgenden Leerzeichen; z[:1] enfernt das Leerzeichen am Ende
und die int-Funktion wandelt diese String dann in einen integer um. Die in dieser
Listenkomprehension enthaltenen Zahlen können dann mittels der sum-Funktion aufsummiert
werden.

__________________________________________________________________________________________________________________

Aufgabe 4.2


(a)

Verwenden Sie re.findall, um zu bestimmten, wie viele Ziffern die Datei test.txt
enthält.

(b)

Verwenden Sie re.findall, um zu bestimmten, wie viele aus Groß- und/oder
Kleinbuchstaben bestehenden Wörter die Datei test.txt enthält.

(c)

Verwenden Sie re.findall, um alle „inneren Klammerungen“ der Datei test.txt
zurückzuliefern.
Aus dem String (Hallo (Welt)), (hier (bin (ich))) sollten also die Strings
[(Welt)(ich)] extrahiert werden.

(d)

Verwenden Sie re.findall, um zu bestimmen, wie viele Klammerpaare es in der Datei
test.txt insgesamt gibt.

Lösung

(a)

Anzahl der Ziffern in der Datei test.txt:

 
len(re.findall(r'[0-9]',open(test.txt).read()))
    

re.findall(r[0-9],str) liefert die Liste aller in str enthaltenen Ziffern. Gefragt war die
Länge dieser Liste.

(b)

Anzahl der aus Groß- und/oder Kleinbuchstaben bestehenden Wörter in der Datei
test.txt:

 
tst = open('test.txt').read() 
len(re.findall(r'[A-Za-z]+[^A-Za-z]',tst))
    

Hier sieht man schön, dass reguläre Ausdrücke immer nach dem längsten möglichen Match
suchen.

(c)

Anzahl der Klammerpaare in test.txt gesamt:

 
tst = open('test.txt').read() 
len(re.findall(r'[\(\)]',tst)) / 2
    

In eckigen Klammern ist immer eine Auswahl von Zeichen aufgeführt, die matchen. Der
reguläre Ausdruck r[\(\)] matcht folglich alle Klammern. Um die Anzahl der
Klammerpaare zu berechnen, muss nur noch die Gesamtzahl der Klammern durch zwei
geteilt werden.

_____________________________________________________________________________________

Aufgabe 4.3


(a)

Verwenden Sie das re.sub-Kommando, um alle Kommentare in der Datei test.py zu
entfernen.

(b)

Verwenden Sie das re.sub-Kommando, um alle Zeilen der Datei test.py
auszukommentieren.

Lösung

Beide Lösungen verwenden die Option re.MULTILINE, mit der der String mehrzeilig
interpretiert wird, d. h. das Muster r^ matcht den Anfang des betreffenden Strings und
jedes Zeichen nach dem Newline-Zeichen \n. Entsprechendes gilt für das Muster
r$.

(a)

Entfernen aller Kommentare in der Datei test.py – sobald das Zeichen # gematcht
wurde, wird der Rest der Zeile bis zum Zeilenende durch den leeren String ersetzt.

 
>>>tst = open('test.txt').read() 
>>>re.sub(r'#.*$','',tst,re.MULTILINE)
    
(b)

Auskommentieren aller Zeilen der Datei test.txt:

 
>>>re.sub('^','#',tst,re.MULTILINE)
    

__________________________________________________________________________________________________________________

Aufgabe 4.4


Die Lösung löscht jedoch keine Dreier-, Vierer- usw. -Vorkommen von Wörtern.
Erweitern Sie die Lösung entsprechend so, dass auch diese Fälle bereinigt werden.

Lösung

Löschen von n-Vorkommen von Wörtern in Strings:

 
>>>re.sub(r'([a-zA-Z]+\s+)\1+',r'\1',string)
   

Das matcht aber nicht, wenn Doppelvorkommen mit unterschiedlich vielen Leerzeichen
getrennt sind. Bessere Lösung:

 
re.sub(r'([a-zA-Z]+)\s+(\1\s+)+',r'\1 ',string)
   

__________________________________________________________________________________________________________________

Aufgabe 4.5


(a)

Verwenden Sie das re.sub-Kommando, um alle runden Klammern in einem String zu
entfernen.

(b)

Verwenden Sie das re.sub-Kommando, um alle inneren runden Klammern in einem
String zu entfernen.

(c)

Verwenden Sie das re.sub-Kommando, um alle objektorientierten Methodenaufrufe
in Funktionsaufrufe um zu wandeln, die das Objekt als erstes Argument erhalten.
Beispiel: xy.findall(pat,str) soll umgewandelt werden
in findall(xy,pat,str) oder [1,2,3].count(1) soll umgewandelt werden in
count([1,2,3],1).

Lösung

(a)

Entfernen von allen runden Klammern in einem String: Wir ersetzen r[\(\)] einfach
immer durch den leeren String – der Backslash vor den runden Klammern ist übrigens
unbedingt notwendig, denn die runden Klammern haben immer eine Sonderbedeutung,
nämlich die Gruppierung.

 
re.sub(r'[\(\)]',r'',string)
    
(b)

Folgendermaßen kann man ausschließlich die inneren runden Klammern entfernen:

 
re.sub(r'\(([^\)\(]*)\)',r'\1',string)
    
(c)

Wir wandeln alle OO-Methodenaufrufe in normale Funktionsaufrufe um:

 
re.sub(r'([a-zA-Z]\w+)\.([a-zA-Z]\w+)\(',r'\2(\1,',string)
    

_____________________________________________________________________________________

Aufgabe 4.6


Was liefert der Aufruf m.group(3) als Ergebnis? – Erklären Sie!

Lösung

Ein Aufruf von m.group(3) liefert:

 
>>>m.group(3) 
>>>',3'
   

Erklärung: Das Ergebnis bezieht sich auf die dritte Gruppierung im regulären Ausdruck
r\[([^,\]]+)((,[^,\]]+)*)\] – nämlich (,[^,\]]+); dieser reguläre (Teil-)Ausdruck
matcht ein Komma, gefolgt vom darauffolgenden Listenelement. Dieser reguläre
(Teil-)Ausdruck wird wiederholt gematch, da auf ihn der *-Operator angewendet wird.
Der Aufruf m.group(3) liefert einfach den letzten Match zurück, sprich: das letzte
Listenelement.

__________________________________________________________________________________________________________________

Aufgabe 4.7


Sie möchten alle Wörter matchen, die mit einem ‚e‘ beginnen und mit einem ‚e‘ enden.
Erklären Sie, was dagegen spricht, den regulären Ausdruck


r\be.*e\b


zu verwenden?

Lösung

Der reguläre Ausdruck r\ba.*a\b matcht eben nicht alle Wörter, die mit einem a
beginnen und mit einem a enden. Der Teil r\ba matcht ein Wortanfang a. Der
darauffolgende Teil r.*ab matcht jedoch eine möglichst lange Folge beliebiger Zeichen (auch
solcher an Wortenden). Im String


das eine kleine Ding und das andere kleine Ding.


matcht also der reguläre Ausdruck r\ba.*a\b den String


eine kleine Ding und das andere kleine

_________________________________________________________________________

Aufgabe 4.8


Geben Sie einen regulären Ausdruck an, der …

(a)

…alle Zeilen matcht, die genau ein Wort enthalten.

(b)

…alle Worte matcht, die kein a enthalten.

(c)

…alle Worte matcht, die mit einem a beginnen.

Lösung

(a)

Alle Zeilen, die genau ein Wort enthalten: r^\W*\w+\W*$ – also am Anfang der Zeile
eine beliebige (möglicherweise auch leere) Folge nicht alpha-numerischer Zeichen,
gefolgt von dem eigentlcihen Wort (eine Folge alphanumerischer Zeichen), gefolgt von
einer beliebigen (möglicherweise auch leeren) Folge alphanumerischer Zeichen.

(b)

Alle Worte, die keine a enthalten: r\b[^a\W]+\b – mit \b wird der
String-Anfang bzw. das String-Ende gematcht; [^a\W] stellt sicher, dass der String
kein a und keine nicht-alphanumerischen Zeichen enthält.

(c)

Alle Worte, die mit einem a beginnen: r\ba\w*\b.

_____________________________________________________________________________________

Aufgabe 4.9


Zählen Sie alle Strings auf, die durch den regulären Ausdruck


r(ab?)?a?


gematcht werden.

Lösung

Alle Strings, die durch den regulären Ausdruck r(ab?)?a? gematcht werden: , a, ab,
aba

_________________________________________________________________________

Aufgabe 4.10


Geben Sie einen regulären Ausdruck an, der …

(a)

…alle Worte matcht, die mindestens 3 und höchstens 5 Buchstaben haben.

(b)

…alle Worte matcht, die genau ein a enthalten.

(c)

…alle Worte matcht, die genau zwei a enthalten.

Lösung

(a)

Alle Worte, die mindestens 3 und höchstens 5 Buchstaben haben: r\b\w{3,5}\b
\b matcht den Wortanfang; \w{3,5} matcht eine Folge von mindestens 3 und
höchstens 5 alphanumerischen Zeichen.

(b)

alle Worte, die genau ein a enthalten: r\b[^a\W]*a[^a\W]*\b – nach
dem Wortanfang \b folgt eine (möglicherweise leere) Folge alphanumerischer
nicht-a-Zeichen, dann das a, dann wiederum eine beliebig lange (möglicherweise
auch leere) Folge alphanumerischer nicht-a-Zeichen.

(c)

alle Worte, die genau zwei a enthalten: \b[^a\W]*a[^a\W]+a[^a\W]*\b.

_____________________________________________________________________________________

Aufgabe 4.11


Geben Sie einen regulären Ausdruck an, der …

(a)

…alle Worte matcht, die mehr als 10 Zeichen enthalten.

(b)

…alle Zeilen matcht, die ein Wort enthalten das eine Länge von mindestens 10 Zeichen
hat.

(c)

…alle Zeilen matcht, die keine Worte enthalten, die eine Länge von mehr als 5 Zeichen
haben.

Lösung

(a)

alle Worte, die mehr als 10 Zeichen enthalten: r\b\w{10}\w*\b

(b)

alle Zeilen, die ein Wort enthalten das eine Länge von mindestens 10 Zeichen hat:
r^.*\b\w{10}\w*\b.*$

(c)

alle Zeilen, die keine Worte enthalten, die eine Länge von mehr als 5 Zeichen haben:

Zunächst: Der reguläre Ausdruck r\b\w{1,5}\b matcht alle Wörter die höchstens
5 Zeichen haben. Möchte man, dass eine Zeile ausschließlich aus solchen Worten
besteht, so muss man alle anderen Möglichkeiten ausschließen:


r^[^\w\n]*(\b\w{1,5}\b[^\w\n]+)*[^\w\n]*$

_____________________________________________________________________________________

Aufgabe 4.12


Wahr oder falsch? – Bitte begründen Sie jeweils.

(a)

r(re)* matcht genau die gleichen Strings wie r(re)*(re)?

(b)

r[re]* matcht genau die die gleichen Strings wie r((re)?)*

(c)

r[re]* matcht genau die gleichen Strings wie r(r?e?)*

(d)

r((re)*)* matcht genau die gleichen Strings wie rre*

(e)

r((re)*)* matcht genau die gleichen Strings wie r(re)*

(f)

r(a?b?)? matcht genau die gleichen Strings wie ra?b?

Lösung

(a)

r(re)* r(re)*(re)? (der leere String wird von dem linken RegExp gematcht,
nicht aber von dem rechten.

(b)

r[re]* r((re)?)* (der String r wird von dem linken RegExp gematcht,
nicht aber von dem rechten.

(c)

r[re]* = r(r?e?)*

(d)

r((re)*)* rre* (der String r wird von dem rechten RegExp gematcht, nicht
aber von dem linken.

(e)

r((re)*)* = r(re)*

(f)

r(a?b?)? = ra?b?

_____________________________________________________________________________________

Aufgabe 4.13


(a)

Geben Sie einen regulären Ausdruck s an, der alle äußeren Klammerungen (mit runden
Klammern) matcht. Beispielsweise sollte gelten:

 
>>>re.findall(s,'(al(l)e) Klammern (sollten (ge(funden))) werde(n)'>>>['(al(l)e)''(sollten (ge(funden)))''(n)']
    
(b)

Geben Sie einen regulären Ausdruck s an, der alle inneren Klammerungen (mit runden
Klammern) matcht. Beispielsweise sollte gelten:

 
>>>re.findall(s,'(al(l)e) Klammern (sollten (ge(funden))) werde(n)'>>>['(l)''(funden)''(n)']
    

Lösung

(a)

Genau hier stoßen wir an die Grenzen dessen, was reguläre Ausdrücke leisten können.
Ein regulärer Ausdruck kann sich nicht “merken”, wieviele öffnenden uns schließende
Klammern es bisher gab; daher kann ein regulärer Ausdruck auch nicht die äußern

Klammerpaare suchen – hierfür müsste man Buch führen darüber wieviele offene
Klammern es an einer bestimmte Stelle im String gibt. Das Finden der inneren
Klammerpaare ist dagegen recht einfach möglich.

(b)

Folgendermaßen erhält man alle “inneren Klammerungen” des in der Aufgabenstellung
beschriebenen Strings string1.

 
>>>s = r'\([^()]*?\)' 
>>>re.findall(s,string1['(l)''(funden)''(n)']
    

Der reguläre Ausdruck r\( matcht eine öffnende Klammer; der reguläre Ausdruck
r[^\(\)]* matcht eine (möglichst lange) Folge von Zeichen, die keine Klammern
enthält; der Match muss schließlich mit einer schließenden Klammer enden.

_____________________________________________________________________________________

Aufgabe 4.14


Geben Sie einen regulären Ausdruck an, der …

(a)

…alle Worte matcht, die entweder mit einem a anfangen oder mit einem z enden.

(b)

…alle Zeilen matcht, die entweder ein a oder ein z enthalten.

Lösung

(a)

alle Worte, die entweder mit einem a anfangen oder mit einem z enden:
\b(a\w*|\w*z)\b

(b)

alle Zeilen matcht, die entweder ein a oder ein z enthalten: ^.*[az].*$
funktioniert allerdings nur, wenn das re.MULTILINE-Flag gesetzt ist.

_____________________________________________________________________________________

Aufgabe 4.15


Geben Sie einen regulären Ausdruck an, der …

(a)

…Zahlen zwischen 1 und 24 matcht.

(b)

…Zahlen zwischen 1 und 31 matcht.

(c)

…Zahlen zwischen 1 und 365 matcht.

(d)

…Zahlen zwischen 0 und 59 matcht.

Lösung

(a)

Zahlen zwischen 1 und 24:

 
regexp = r'\b(2[0-4]|1[0-9]|[1-9])\b'
    
(b)

Zahlen zwischen 1 und 31:

 
regexp = r'\b(3[01]|[12][0-9]|[1-9])\b'
    
(c)

Zahlen zwischen 1 und 365:

 
regexp = r'\b(36[0-5]|3[0-5][0-9]|[12][0-9][0-9]|[1-9][0-9]|[1-9])\b'
    
(d)

Zahlen zwischen 0 und 59:

 
regexp = r'\b[1-5]?[0-9]\b'
    

_____________________________________________________________________________________

Aufgabe 4.16


(a)

Erweitern Sie das re.sub-Kommando im letzten Beispiel so, dass ein Klammereinschub
am Ende eines Satzes lediglich durch einen Gedankenstrich ersetzt wird. So sollte
beispielsweise der String


Alle (bis auf wenige) werden ersetzt (ok)? Gut so.


ersetzt werden durch


Alle  bis auf wenige  werden ersetzt  ok? Gut so.

(b)

Funktioniert der reguläre Ausdruck im letzten Beispiel auch mit geschachtelten Klammerausdrücken?
Was passiert, wenn das re.sub-Kommando aus diesem Beispiel auf den String


Das sind (sollte man aber (fast) nie machen) Schachtelungen


anwendet?

Lösung

Die funktioniert am einfachsten über die Ausführung von zwei re.sub-Kommandos; das erste
re.sub-Kommando verwendet einen regulären Ausdruck, der nur Klammerausdrücke am
Ende eines Satzes matcht – dies wird sichergestellt durch die zweite Gruppierung
([?.;])’!

 
>>>string = 'Alle (bis auf wenige) werden ersetzt (ok)? Gut so.' 
>>>string = re.sub(r'\(([^)]*)\)([?.;!])',r'-- \1\2',string>>>re.sub(r'\(([^)]*)\)',r'-- \1 --',string'Alle -- bis auf wenige -- werden ersetzt -- ok? Gut so.'
   

__________________________________________________________________________________________________________________

Aufgabe 4.17


Geben Sie einen regulären Ausdruck an, der alle Worte matcht, die entweder kein a oder
kein z enthalten.

Lösung

Wir wollen alle Worte matchen, die entweder kein a oder kein z enthalten. Ein
Wort, das also sowohl ein a als auch ein z enthält, darf also nicht gematcht
werden. Was ist mit Wörtern, die weder a noch z enthalten? – Ob diese gematcht
werden sollen oder nicht hängt davon ab, ob “entweder …oder” ein logischen OR oder
ein logisches XOR (exklusives oder) dargestellt. Wir spielen beide Möglichkeiten
durch:

1.
logisches XOR, d. h. ein Wort das sowohl ein a als auch ein z enthält darf nicht
gematcht werden und ein Wort, das weder ein a noch ein z enthält darf nicht
gematcht werden – alle anderen Wörter schon. Diese Wörter können durch folgenden
regulären Ausdruck spezifiziert werden:

 
r'\b(([^z\W]*a[^z\W]*)|([^a\W]*z[^a\W]*))\b'
       

Des besseren Verständnisses halber fügen wir Kommentare ein:

 
s = r'''\b             # Wortanfang 
      (([^z\W]*a[^z\W]*) # 1. Moegl: ein a und kein z 
      |               # ... oder ... 
      ([^a\W]*z[^a\W]*)) # 2. Moegl: ein z und kein a 
      \b               # Wortende 
      '''
       

Beispielanwendung:

 
>>>re.findall(s,'aber die Katze zielt'>>>[('aber''aber'''), ('zielt''''zielt')]
       

Man sieht: es werden nur die Wörter aber und zielt gematcht. (re.findall liefert
übrigens 3-Tupel zurück, da der reguläre Ausdruck drei Gruppen umfasst – zu sehen an
den drei enthaltenen Klammerpaaren.)

2.
logisches OR, d. h. ein Wort, das sowohl ein a als auch ein z enthält, darf also nicht
gematcht werden – alle anderen schon. Die Lösung ist der Lösung in der vorigen
Teilaufgabe ähnlich – nur dass eine weitere Alternative eingefügt werden muss, nämlich
r[^az\W]+, d. h. ein regulärer Ausdruck, der ein Wort matcht, das weder a noch z
enthält:

 
r'\b(([^z\W]*a[^z\W]*)|([^a\W]*z[^a\W]*)|[^az\W]+)\b'
       

__________________________________________________________________________________________________________________

Aufgabe 4.18


Fügen Sie an alle Referenzen der Form \ref{⟨label⟩} einer LATEX-Datei zusätzlich eine
Seitenreferenz der Form \pageref{⟨label⟩} an.

Lösung

Folgendermaßen kann man eine gängige LATEX-Referenz durch eine Seitenreferenz
ergänzen:

 
>>>text = open(datei).read() 
>>>re.sub(r'\\ref{([^}]+)}',r'\\ref{\1}\\pageref{\1}',text)
   

__________________________________________________________________________________________________________________

Aufgabe 4.19


In Bezug auf obiges Beispiel, das zeigte wie man Paragraphen einer HTML-Datei matcht:
Welche Matches würde es im Text

 
'<p> Erster Paragraph </p> <p> Zweiter Paragraph </p>'
   

geben, wenn versehentlich die greedy-Variante des Wiederholungsoperators * verwendet
worden wäre?

Lösung

Der reguläre Ausdruck <p>.*</p> unter Verwendung des Greedy-Wiederholungsoperators
*, würde nur einen Match ergeben, nämlich:

 
['<p> Erster Paragraph </p> <p> Zweiter Paragraph </p>']
   

__________________________________________________________________________________________________________________

Aufgabe 4.20


Erklären sie folgende Ausgabe:

 
>>>re.findall(r'r??e??','re'['''''']
   

Lösung

Der Wiederholungsoperator ?? ist die non-greedy-Variante des Wiederholungsoperators ?,
d. h. ?? matcht den vorhergenden regulären Ausdruck entweder einmal oder keinmal; der
vorhergende reguläre Ausdruck wird jedoch ausschließlich dann “einmal” gematcht,
wenn keine andere Möglichkeit besteht, d. h. wenn ansonsten kein Match möglich
wäre. In allen anderen Fällen wird der leere String gematcht. Genau das zeigt die
Ausgabe

 
['','','']
   

An jeder möglichen Position des String wird der leere String gematcht!

Nur wenn unbedingt notwendig, wird der vorige reguläre Ausdruck einmal gematcht, wie
etwa in folgendem Beispiel:

 
>>>re.findall(r'r??e??gexp','ein regexp'['regexp']
   

__________________________________________________________________________________________________________________

Aufgabe 4.21


Gibt es einen String, der durch den regulären Ausdruck r?e? einen Match liefert, nicht
jedoch mit r??e???

Lösung

Nein. Wenn r?e? matcht (egal ob das jeweilige ? das vorige Zeichen einmal oder keinmal
matcht), dann liefert immer r??e?? auch einen Match – jedoch nicht unbedingt
denselben!

__________________________________________________________________________________________________________________

Aufgabe 4.22


Geben Sie einen regulären Ausdruck an, der …

(a)

…alle Wörter matcht, die nicht mit einem a beginnen.

(b)

…alle Wörter matcht, denen ein großgeschriebenes Wort folgt.

(c)

…alle Wörter matcht, außer dem Wort Wort.

(d)

…alle Wörter matcht, die nicht die Zeichenfolge „reg“ als Teilwort enthalten.

(e)

…alle Wörter matcht, die entweder mit re oder erg beginnen, das Teilwort reg
enthalten und eine Länge von höchstens 10 haben.

(f)

…alle Wörter matcht, die von Whitespace-Zeichen eingeschlossen sind.

(g)

…alle Wörter matcht, die nicht von Whitespace-Zeichen eingeschlossen sind.

Lösung

(a)

alle Wörter, die nicht mit einem a beginnen:

 
regexp = r'\b[^a\W]\w*\b'}
    
(b)

alle Wörter, denen ein großgeschriebenes Wort folgt.

 
regexp = r'\b\w+\b(?=\W+[A-Z])'
    
(c)

alle Wörter, außer dem Wort Wort.

 
regexp = r'\b(?!Wort\b)\w+\b'
    
(d)

alle Wörter, die nicht die Zeichenfolge “reg” als Teilwort enthalten.

 
regexp = r'\b(?!\w*ei\w*\b)\w+\b'
    
(e)

alle Wörter, die entweder mit re oder erg beginnen, das Teilwort reg enthalten und eine
Länge von höchstens 10 haben.

 
regexp = r'\b(?=re|erg)(?=\w*reg\w*\b)\w{1,10}\b'
    
(f)

alle Wörter, die von Whitespace-Zeichen eingeschlossen sind.

 
r'(?<=\s)\w+(?=\s)'
    
(g)

alle Wörter, die nicht von Whitespace-Zeichen eingeschlossen sind.

 
r'(?<!\s)\w+(?!\s)'
    

_____________________________________________________________________________________

Aufgabe 4.23


Geben Sie einen regulären Ausdruck ohne Lookahead-Operatoren an, der alle Worte matcht,
die mit einem ein oder da oder w beginnen und deren Länge zwischen 5 und 7 Zeichen
beträgt.

Lösung

Ein regulärer Ausdruck ohne Lookahead-Operatoren, der alle Worte matcht, die mit einem
ein oder da oder w beginnen und deren Länge 5 und 7 Zeichen ist.

Will man keinen Lookahead-Operator verwenden, so muss jeder Fall getrennt betrachtet
werden:

 
regexp = r'\b(ein\w{2,4}|da\w{3,5}|w\w{4,6})\b'
   

__________________________________________________________________________________________________________________

Aufgabe 4.24

 

Eine Möglichkeit, in einem LATEX-Dokument Umlaute zu erzeugen ist die, das Zeichen
vor den entsprechenden Nicht-Umlautvokal zu stellen: Ein „Ü“ wird in LATEX also durch
die Zeichenfolge „U erzeugt; ein „ä“ wird in LaTeX durch Zeichenfolge „a erzeugt,
usw.

Angenommen, Sie wollen alle Wörter in einem LATEX-Dokument finden. Der
reguläre Ausdruck r\b\w+\b ist aber leider wegen der möglicherweise in einem
Wort enthaltenen -Zeichen nicht geeignet. Geben Sie einen geeigneten regulären
Ausdruck an, der alle in einem LATEX-Dokument enthaltenen Wörter matcht.

Lösung

Wörtersuche in einem LATEX-Dokument, in dem Umlaute mittels vokalerzeugt werden.
Der folgende reguläre Ausdruck matcht Wörter:

 
regExp = r'(?<=[^"\w])["\w]+(?=$|[^"\w])'
   

Der Lookbehind (?<=[^“w]) stellt sicher, dass ein regulärer Wortanfang gematcht
wird, d. h. das erste Zeichen des Wortes sollte weder ein Wort-Zeichen \w noch ein
Anführungszeichen sein. Der darauffolgende reguläre Ausdruck [„\w]+ matcht eine
Zeichenfolge, die das Anführungszeichen einschließt. Durch den Lookahead (?=[^“\w])
wird das Wortende gematcht, das (anders als üblich) das Anführungszeichen nicht
einschließt.

__________________________________________________________________________________________________________________

Aufgabe 4.26


Passen Sie die Lösung so an, dass alle in einem LATEX-Dokument enthaltenen Worte
gefunden werden, die den String re enthalten und eine Länge zwischen drei und acht haben.
Gehen Sie davon aus, dass Umlaute durch ein dem entsprechenden Vokal vorangestelltem
doppelten Anführungszeichen dargestellt sind, d. h. die Darstellung eines „ü“ erfolgt im
LATEX-Quelltext durch die Zeichen „u, die Darstellung eines „Ä“ durch die Zeichen „A, usw.

Lösung

LaTeX-Wörter kann man durch folgendes re.split-Kommando erhalten:

 
>>>re.split(r'[^"\w]+',tst)
   

Alle Wörter, die re enthalten und eine Länge zwischen 3 und 8 haben kann man also
folgendermaßen erhalten:

 
>>>[wort for wort in re.split(r'[^"\w]+',open('test.txt').read()) 
       if 're' in wort and len(wort)and len(wort)8]
   

__________________________________________________________________________________________________________________

Aufgabe 5.4


(a)

Verwenden Sie eine Schleife, um mittels fetchone() alle Zeilen der Ergebnismenge in
einer Liste zu speichern.

(b)

Schreiben Sie eine Funktion myexecute, die einen Cursor und ein SQL-Statement
übergeben bekommt und falls sich mehr als 1000 Zeilen in der Ergebnismenge
befinden, das fetchall-Kommando verwendet, um die Ergebnisse auf dem Bildschirm
auszugeben und – falls sich mehr als 1000 Zeilen in der Ergebnismenge befinden
– mittels fetchone die Stringrepräsentationen der Zeilen der Ergebnismenge in der
Datei myexecute.txt speichert.

Lösung

(a)

Folgendermaßen kann man mittels fetchone alle Zeilen der Ergebnismenge in einer Liste
speichern:

 
>>>cursor.execute('SELECT * FROM Student'>>>erg = [] 
>>>while True: 
...  row = cursor.fetchone() 
...  if row==None: break 
...  erg.append(row)
    
(b)

Die Funktion myexecute behandelt lange Ergebnislisten anders als kurze Ergebnislisten:

 
def myexecute(curstatement): 
 if cur.execute(statement)1000: 
   print cur.fetchall() 
 else   dat = open('myexecute.txt','w'   while True: 
    zeile = cur.fetchone() 
    if row == None: break 
    dat.write(str(zeile)+'\n')
    

_____________________________________________________________________________________

Aufgabe 5.5


Geben Sie ein SELECT-Statement an, das …

(a)

…die Vor- und Nachnamen aller Studenten liefert, die seit mehr als 3 Jahren
immatrikuliert sind.

(b)

…die Matrikelnummern aller Studenten liefert, deren Nachname mit Z beginnt.

(c)

…die Namen aller Studenten des Studiengangs ST liefert, deren Nachname mit M
beginnt und mit r endet und ein e oder a enthalten und die seit mindestens 2 Jahren
immatrikuliert sind.

(d)

…alle Vorlesungstitel des Professors mit der Personalnummer 5321 liefert, die mit P
beginnen.

Lösung

(a)

die Vor- und Nachnamen aller Studenten, die seit mehr als 3 Jahren immatrikuliert
sind:

 
SELECT Vorname, Nachname FROM Student 
 WHERE YEAR(CURDATE)-YEAR(Imm) > 3;
    
(b)

die Matrikelnummern aller Studenten, deren Nachname mit Z beginnt:

 
SELECT Matrikelnr FROM Student 
 WHERE Nachname REGEXP '^Z';
    
(c)

die Namen aller Studenten des Studiengangs ST, deren Nachname mit M beginnt und mit r
endet und ein e oder a enthalten und ein r enthalten und die seit mindestens 2 Jahren
immatrikuliert sind.

 
SELECT Vorname, Nachname FROM Student 
 WHERE Studiengang = 'KST' 
      AND Nachname REGEXP '^M.*[ea].*r$' 
      AND YEAR(CURDATE())-YEAR(Imm)>=2;
    
(d)

alle Vorlesungstitel des Professors mit der Personalnummer 5321, die mit P beginnen.

 
SELECT Titel FROM Vorlesung 
 WHERE Prof = 5321 AND Titel REGEXP '^P'
    

_____________________________________________________________________________________

Aufgabe 5.7


Verwenden Sie ein SQL-SELECT-Statement, um folgende Fragen zu beantworten:

(a)

Wie viele Studenten hat der Studiengang TI?

(b)

Welcher Studiengang hat die meisten Studenten?

(c)

Wie viele Professoren haben die einzelnen Studiengänge?

(d)

Wie viele Studenten nehmen an den einzelnen Vorlesungen teil?

(e)

Wie ist das Betreuungsverhältnis im Studiengang BKT?

(f)

Welche Vorlesungen hört Student Noam Chomsky?

(g)

Welche Studenten haben sich vor mehr als 5 Jahren eingeschrieben?

(h)

Mit welchen Studenten hat der Prof’in Eva Lang über Ihre Vorlesungen zu tun?

(i)

Welche Studenten hören Vorlesungen von studiengangsfremden Professoren?

(j)

Die fleißigen Studenten: Alle Studenten die Vorlesungen höherer Semester hoeren

(k)

Die Nachzügler: Alle Studenten die Vorlesungen niederer Semester hoeren.

Lösung

(a)

“Wieviele Studenten hat der Studiengang TI?”

 
SELECT COUNT(*) 
 FROM Student 
 WHERE Studiengang = 'TI';
    
(b)

“Welcher Studiengang hat die meisten Studenten?” Entweder folgendermaßen:

 
>>>cursor.execute('''SELECT Studiengang, COUNT(*) AS Anz 
                  FROM Student 
                  GROUP BY Studiengang 
                  ORDER BY Anz DESC'''5L 
>>>cursor.fetchone()[0] 
'BKT'
    

… oder mittels der SQL-MAX-Funktion.

(c)

Wieviele Professoren haben die einzelnen Studiengänge?

 
SELECT Studiengang, COUNT(*) as Anz 
 FROM Professor 
 GROUP BY Studiengang;
    
(d)

Wie viele Studenten nehmen an den einzelnen Vorlesungen teil?

 
SELECT Vorlesung.Titel, COUNT(*) 
 FROM Vorlesung, VLStudent 
 WHERE Vorlesung.Nr = VLStudent.VL 
 GROUP BY Vorlesung.Titel;
    
(e)

 
>>>cursor.execute('SELECT COUNT(*) FROM Student WHERE Studiengang="BKT"'1L 
>>>anzStuds = cursor.fetchone()[0] 
>>>cursor.execute('''SELECT COUNT(*) FROM Professor 
                 WHERE Studiengang="BKT" ORDER BY Studiengang'''1L 
>>>anzProfs = cursor.fetchone()[0] 
>>>anzStuds / anzProfs # Betreuungsverhaeltnis 
>>>32L
    

Wir haben also im Studiengang BKT ein Betreuungsverhältnis von 32 Studenten pro
Professor.

(f)

Welche Vorlesungen hört Student Noam Chomsky?

 
SELECT Vorlesung.Titel 
 FROM Student, Vorlesung, VLStudent 
 WHERE Student.Nachname = 'Chomsky' 
      AND VLStudent.Student = Student.Matrikelnr 
      AND Vorlesung.Nr = VLStudent.VL;
    
(g)

Welche Studenten haben sich vor mehr als 5 Jahren eingeschrieben?

 
SELECT Nachname 
FROM Student 
WHERE YEAR(CURDATE())-YEAR(Imm)>5
    
(h)

Mit welchen Studenten hat der Prof’in Eva Lang über Ihre Vorlesungen zu tun?

 
SELECT Student.Nachname 
 FROM Professor, Student, VLStudent, Vorlesung 
 WHERE Professor.Personalnr = Vorlesung.Prof AND 
      Student.Matrikelnr = VLStudent.Student AND 
      Vorlesung.Nr = VLStudent.VL AND 
      Professor.Vorname = 'Eva' AND 
      Profesor.Nachname = 'Lang';
    
(i)

Welche Studenten hören Vorlesungen von studiengangsfremden Professoren?

 
SELECT Student.Nachname 
 FROM Student, Professor, VLStudent, Vorlesung 
 WHERE Student.Matrikelnr = VLStudent.Student AND 
      VLStudent.VL = Vorlesung.Nr AND 
      Vorlesung.Prof = Professor.Personalnr AND 
      Professor.Studiengang != Student.Studiengang;
    
(j)

Die fleißigen Studenten: Alle Studis die VLs hoeherer Semester hoeren?

(k)

Die Nachzügler: Alle Studis die VLs niederer Semester hoeren.

_____________________________________________________________________________________

Aufgabe 5.12


Wie im Text erläutert, wird durch obige Tabelle „Autor“ die 3. Normalform verletzt. Die
Normalformen sollen ja immer auch sicherstellen, dass keine Dateninkonsistenzen auftreten
können.
Beschreiben Sie, welche Inkonsistenzen durch das Schema der Tabelle „Autor“ entstehen
können.

Lösung

Man kann einfach dadurch Inkonsistenzen “generieren”, in dem man in die Tabelle das
Werte-Tupel (126,Ute,Bock,89134,Ulm) einfügt; man hat nun zwei verschiedene
Städtenamen unter einer einzigen PLZ.

__________________________________________________________________________________________________________________

Aufgabe 5.15


Betrachten wir folgendes in der Variablen adict gespeichertes Python-Dictionary-Objekt:

 
>>>adict = { 'name' : 'knuth' , 'job' : 'computer scientist' }
   

Gilt str(adict) == json.dumps(adict) ?

Lösung

Fast – bis auf eine Kleinigkeit: JSON-Strings sind immer in doppelte Anführungszeichen
eingeschlossen, Pythons str-Funktion dagegen verwendet üblicherweise einfache
Anführungszeichen für die Darstellung von Strings.

__________________________________________________________________________________________________________________

Aufgabe 5.17


Sollte die Datenbank test schon existieren, dann bricht das in Listing ?? gezeigte Skript
ab. Fügen Sie mittels try und except ein einfaches Exception-Handling ein, das
diesen möglichen Fehler folgendermaßen abfängt: Sollte die CouchDB schon eine
Datenbank mit Namen test haben, so soll eine kurze Fehlermeldung ausgegeben
werden, anschließend soll diese Datenbank gelöscht werden (Pythons del-Funktion
kann auf das srv-Objekt angewendet werden) und schließlich neu erzeugt werden.

Lösung

Folgendermaßen kann das in Listing ?? gezeigte Python-Skript robuster gestaltet
werden:

 
srv = Server('http://localhost:5984' 
database = 'test' 
try mydb = srv.create(databaseexcept Exceptione print 'Exception ',e,' deleting then creating database' 
 del srv[database mydb = srv.create(database 
...
   

Schlägt das erzeugen der Datenbank in Zeile 5 fehl, so wird diese Datenbank gelöscht
und anschließend neu erzeugt. Das restliche Skript kann analog wie in Listing ??
erfolgen.

__________________________________________________________________________________________________________________

Aufgabe 7.3


Zwar ist durch das in Listing ?? gezeigte Skript die Gefahr, dass die Bildschirmausgabe eines
Threads durch die Bildschirmausgabe eines anderen Threads unterbrochen wird, weitgehend
gebannt. Es sind jedoch nach wie vor Situationen denkbar, in denen es zu einer solchen
unerwünschten Unterbrechung kommen kann.

(a)

Beschreiben Sie eine Situation während der Ausführung des in Listing ?? gezeigten
Skripts, in der die Bildschirmausgabe eines Threads durch die Bildschirmausgabe
eines anderen Threads unterbrochen werden kann.

(b)

Passen Sie das in Listing ?? gezeigte Skript so an, dass alle Unterbrechung von
Bildschirmausgaben ausgeschlossen werden.

Lösung

(a)

  • Die zweite Bildschirmausgabe ist nicht durch einen Lock geschützt und
    entsprechend ist eine Unterbrechung der Bildschirmausgabe der an Zeile 11
    befindlichen print-Anweisung möglich.
  • Es ist sogar denkbar, dass die an Zeile 7 befindliche Bildschirmausgabe durch die
    Bildschirmausgabe eines anderen Threads unterbrochen wird (der gerade Zeile
    11 abarbeitet). Dieses Situation kann auftreten, wenn einer der Threads eine sehr
    kleine Zufallszahl gewählt hat und entsprechend schnell mit dem Aufsummieren
    fertig ist.
(b)

Das in Listing ?? gezeigte Skript kann einfach folgendermaßen angepasst werden:

 
1from random import randint 
2from threading import ThreadLock 
3 
4def sumThread(j,l): 
5 n = randint(100,1000000) 
6 l.acquire() ; print 'Thread',j,'summiert bis',n ; l.release() 
7 i=0 
8 for i in range(n): i+=n 
9 l.acquire() ; print 'Thread',j,': Summe ist',i ; l.release() 
10 
11l = Lock() 
12threads = [Thread(target=sumThreadargs=(j,l)) for j in range(3)] 
13for t in threadst.start()
    
14

Man beachte, dass die Verwendung einer zweiten Lock-Variable für die zweite
Bildschirmausgabe nicht zu einem ausreichenden Schutz vor Unterbrechung führen
würde.

_____________________________________________________________________________________

Aufgabe 7.5


(a)

Was würde das Skript aus Listing ?? ausgeben, wenn man Zeile 16 auskommentieren
würde?

(b)

An welcher Listenposition der Variable erg stehen die Ausgaben desjenigen
Threads der als erstes endete? An welcher Listenposition der Variable erg stehen die
Ausgaben desjendigen Threads der als letztes endete?

Lösung

(a)

Würde man Zeile 16 des Skripts aus Listing ?? auskommentieren, würde das Skript
(höchstwahrscheinlich) einfach

 
[]
    

ausgeben. Grund: Kurz nach dem Starten der Prozesse gab noch keine URL Rückmeldung
und somit wurde noch kein Eintrag in die Variable erg vorgenommen. Aufgrund der
(zumindest für den Anwendungsprogrammierer) nicht vorhersagbaren Zeitzuteilung des
Betriebssystems kann man dies jedoch nicht sicher sagen – zumindest möglich wäre
es (falls ein Thread eine große Zeitscheibe zugeteilt bekäme und dessen URL
zufälligerweise prompt antwortet), dass schon ein Eintrag in erg vorgenommen
wurde.

(b)

Die Daten desjenigen Threads der als erstes endete, befinden sich an Position 0 der Liste
erg; die Daten desjenigen Threads der als letztes endete, befinden sich an Position len(urls)
der Liste erg.

_____________________________________________________________________________________

Aufgabe 7.8


Vergleichen Sie die Laufzeiten der Implementierung aus Listing ?? mit der Laufzeit der
Implementierung aus Listing ?? (wenn irgend möglich) auf einen Mehrkern-Rechner.

Lösung

Vergleich der Laufzeiten auf einem Intel Centrino 2 (2 Cores) Prozessor. Die parellele
Implementierung startet 10 parallele Prozesse um insgesamt 4000 Wiederholungen des
Zufallsexperimentes durchzuführen.

 
> time zufallPar.py 1000 3500 4000 10 
Die Wahrscheinlichkeit ist 0.0075 
 
real  0m4.802s 
user  0m9.285s 
sys  0m0.032s 
> time zufallSeriell.py 1000 3500 4000 
Die Wahrscheinlichkeit ist 0.0075 
 
real  0m9.113s 
user  0m9.097s 
sys  0m0.016s
   

Die parellele Implementierung benötigte also 4.8 Sekunden, die serielle Implementierung
dagegen 9.1 Sekunden; dies entspricht einer Laufzeitverbesserung um fast den Faktor
2.

__________________________________________________________________________________________________________________

Aufgabe 7.9


Wir betrachten folgende Fragestellung aus der Stochastik: „Gegeben seien n Kinder an die
zufällig k Gummibärchen verteilt werden. Wie groß ist die Wahrscheinlicheit, dass dabei
(mindestens) ein Kind leer ausgeht.“

(a)

Implementieren Sie einen seriellen Algorithmus, der dieses Zufallsexperiment M-mal
durchführt.

(b)

Geben Sie mit Hilfe von Pythons multiprocessing-Modul eine
parallele Implementierung an, die P parallel arbeitende Prozesse verwendet um das
Zufallsexperiment M mal durchzuführen.

Die Parameter n, k, M und P sollen dabei jeweils als Kommandozeilenparameter übergeben
werden.

Lösung

(a)

Folgendes Listing zeigt eine serielle Implementierung:


 
1#!/usr/bin/python 
2from random import randint 
3from sys import argv 
4 
5n = int(argv[1]) 
6k = int(argv[2]) 
7wiederhlg = int(argv[3]) 
8 
9zaehler = 0 
10for i in range(wiederhlg): 
11 kind = [0]*n 
12 for j in range(k): kind[randint(0,n-1)]+=1 
13 if 0 in kindzaehler += 1 
14print "Wahrscheinlichkeit ist "float(zaehler)/wiederhlg
    
15
Listing 1: Serielle Implementierung des Zufallsexperimentes k Gummibärchen an n
Kinder zu verteilen.

(b)

Folgendes Listing zeigt eine parallele Implementierung.


 
1#!/usr/bin/python 
2from multiprocessing import ProcessValueLock 
3from random import randint 
4from sys import argv 
5 
6n = int(argv[1]) 
7k = int(argv[2]) 
8M = int(argv[3]) 
9P = int(argv[4]) 
10M = M -M%P 
11 
12def proc(idgesamtgesamtLock): 
13 global n,k,M,P 
14 myM = M/P 
15 zaehler = 0 
16 for i in range(myM): 
17   kind = [0]*n 
18   for j in range(k): kind[randint(0,n-1)]+=1 
19   if 0 in kindzaehler += 1 
20 gesamtLock.acquire() 
21 gesamt.value +zaehler 
22 gesamtLock.release() 
23 #print ”Prozess ”,id, ”endet; zaehler war”, zaehler 
24 
25def main(): 
26 gesamt = Value('i',0) 
27 gesamtLock = Lock() 
28 procLst = [ Process(target=procargs=(i,gesamt,gesamtLock)) for i in range(P)] 
29 for p in procLstp.start() 
30 for p in procLstp.join() 
31 print "Die Wahrscheinlichkeit ist"float(gesamt.value)/M 
32 
33if __name__ == '__main__'main()
    
34
Listing 2: Parallele Implementierung des Zufallsexperimentes k Gummibärchen an
n Kinder zu verteilen.

_____________________________________________________________________________________

Aufgabe 7.13


Welche der Pipelinestufen der Pipeline zur Berechnung von Primzahlen hat i. A. mehr
Rechenarbeit zu leisten, welche weniger Rechenarbeit?

Lösung

Die Pipelinestufe (bzw. der Prozess, was in diesem Falle das selbe ist) P1 hat sicherlich die
wenigste Arbeit:

  • Sie hat weniger Arbeit als die Pipelinestufe P0, da sie nur einen Teil der Zahlen
    prüfen muss, die P1 prüft (die durch 3 teilbaren werden ja aussortiert).
  • Sie hat i. A. auch weniger Arbeit als die Pipelinestufe P2. Zwar muss P2 nur ein
    Teil der Zahlen prüfen (die durch 5 teilbaren werden ja von P1 aussortiert), aber für
    eine Zahl z muss P2 genau √-- z7Schleifendurchläufe durchführen, für z = 900
    sind dies immerhin 23 Schleifendurchläufe. Daher hat also P2 auch in den meisten
    Fällen mehr Arbeit als P0.

___