Perl für Philologen
Warum
sollen Philologen programmieren? Das können doch andere besser. Leider
gibt es bei der Erstellung und Auswertung elektronischer Texte immer wieder
Situationen, in denen Werkzeuge nötig wären, die über die
einfachen Funktionen hinausgehen, die in einer Textverarbeitung angeboten
werden. Aber schon aus praktischen Gründen kann man dann zumeist keinen
professionellen Programmierer bemühen und versucht dann eine Aufgabe mit
höchst unzureichenden Mitteln zu lösen. Da Perl im Augenblick die
einfachste und für die Bearbeitung von Texten mächtigste
Scriptsprache ist, soll von den sehr umfassenden Möglichkeiten Perls eben
soviel vermittelt werden, wie zur Lösung einfacherer Aufgaben notwendig
ist. Die wenigen Stunden, die man braucht, um sich dieses Perl für
Philologen anzueigenen, sind bald wieder gewonnen, wenn man dieses
nützliche Werkzeug in Projekten einsetzen kann.
Erste
Schritte
Perl
ist eine einfache, aber mächtige Scriptsprache, die auf fast allen
Plattformen läuft. Im Gegensatz zu einem kompilierten Programm, das in
einer binären Maschinensprache vorliegt und nur auf dem jeweiligen
Prozessor ablauffähig ist, für den es kompiliert wurde, liegt der
Programmcode von Skriptsprachen wie Perl als ASCII-Datei vor und ist
überall dort ablauffähig, wo der zugehörige Interpreter
läuft.
Um
Perl zu verwenden brauchen Sie also einen Perl-Interpreter und einen
ASCII-Editor, um die Scripte zu erstellen. Beides kann man kostenlos im
Internet finden (Adressen im Anhang).
Um
ein Skript zu erstellen, schreibt man den Programmtext in eine ASCII-Datei.
Name und Endung sind prinzipiell egal, aber es gibt die nützliche
Konvention Perl-Skripte mit der Endung
.pl
zu
versehen.
Um
ein Perl-Skript zu starten, ruft man den Interpreter auf und übergibt dem
Interpreter das gewünschte Skript als Argument beim Aufruf. Der
Interpreter wird mit dem Befehl
perl
gestartet.
Soll etwa das Skript "hallo.pl" gestartet werden, würde die Befehlzeile so
aussehen:
perl
hallo.pl
Nun
ein erstes Skript (die Striche vor und nach dem Skript dienen nur der
Kennzeichnung von Dateianfang und -ende):
________________________________________________
#Das
erste Programm
print
"Hallo Welt";
________________________________________________
Die
erste Zeile des Programms ist lediglich ein Kommentar. Alles, was in der Zeile
eines Perl-Skripts nach dem Zeichen # steht, wird als Kommentar behandelt.
"print"
ist die Perl-eigene Anweisung zur Ausgabe. "Hallo Welt" wird als 'Argument'
der Funktion 'print' bezeichnet.
Jede
Befehlszeile von Perl muß mit einem Semikolon abgeschlossen werden.
Das
Programm schreibt auf den Bildschirm die Worte "Hallo Welt".
Skalare
Variablen
Als
Variablen bezeichnet man Platzhalter in einem Programm, deren Wert im Programm
wechseln kann. In Perl sind Variablen einfach daran zu erkennen, daß sie
jeweils mit einem bestimmten Zeichen beginnen. Es gibt mehrere Gruppen von
Variablen, hier soll erst einmal nur die "skalare Variable" interessieren. Ihr
Bezeichner beginnt mit einem $-Zeichen, z.B.
$name.
Nach
dem $ können Buchstaben und Zahlen stehen. Umlaute und andere
Sonderzeichen sind zu vermeiden (Gültige Namen: $a, $hallo, $TEXT1)
Einer
Variablen kann man mit dem =-Zeichen einen Wert zuweisen:
$name
= "Goethe";
$summe
= 22;
Diese
Zuweisung kann öfter vorgenommen werden:
$zahl
= 1;
$zahl
= 5;
Falls
Sie schon einmal mit anderen Programmiersprachen gearbeitet haben, werden Sie
es vielleicht gewohnt sein, daß man 1. Variablen vor ihrer Verwendung
definieren und 2. ihren Typ festlegen muß. Beides ist in Perl nicht
notwendig. Der Interpreter erschließt aus dem Kontext, ob der Wert einer
Variablen eine Zeichenkette oder eine Zahl ist.
Variablen
können natürlich auch mit Funktionen verwendet werden:
$name
= "Goethe";
print
$name;
Das
Ergebnis dieses Programms ist die Ausgabe des Wortes "Goethe" auf dem
Bildschirm.
Operatoren
Mit
Operatoren können Konstanten oder Variablen verbunden werden. Z.B. kann
man zwei Zahlen mit Operatoren addieren (+), subtrahieren (-), dividieren (/)
und multiplizieren (*). Ebenso können Sie auf diese Weise zwei
Zeichenketten miteinander verbinden (.) oder sogar multiplizieren:
$summe
= 4+7;
$zeile
= "hallo " . "welt"; # bei Ausgabe: hallo welt
$zeile
= "hallo" x 3; # bei Ausgabe: hallo hallo hallo
Ein
wichtige und häufige Verwendung von Operatoren ist das Verändern des
Wertes einer Variablen, wobei die Variable anschließend den neuen Wert
hat.
Bsp.:
$summe
= 4;
$summe
= $summe + 6;
#weist der Variablen $summe den alten Wert um sechs erhöht zu
Besonders
häufig wird der Wert um eins erhöht oder vermindert:
$summe
= $summe + 1;
Kurzform:
$summe++;
$summe
= $summe - 1;
Kurzform:
$summe--;
Benutzereingabe
Es
gibt zwei wichtige Formen der Benutzereingabe in Perl. Zum einen kann man das
Programm anhalten lassen und den Benutzer nach der Eingabe fragen. Zum anderen
kann der Benutzer beim Aufruf des Programms eine wichtige Information für
den Programmablauf als Argument angeben, z.B. den Namen der Datei, die
bearbeitet werden soll.
Interaktive
Benutzereingabe
Die
interaktive Benutzereingabe wird durch die folgenden beiden Befehle erreicht:
#Schreibt
die Eingabeaufforderung auf den Bildschirm
print
"Bitte geben Sie Ihren Namen ein: ";
#Weist
die Benutzereingabe einer Variablen als Wert zu
#STDIN
steht für Standard in, d.i. die Tastatur
$name
= <STDIN>;
Sobald
der Benutzer seine Eingabe mit RETURN abschließt, wird seine Eingabe in
der Variablen (hier also in $name) gespeichert.
Ausf.
Bsp.:
________________________________________________
#Fragt
nach Vorname und Nachname und gibt diese dann aus
print
"Bitte geben Sie Ihren Vornamen ein: ";
$vorname
= <STDIN>;
chop
$vorname;
print
"Bitte geben Sie Ihren Nachnamen ein: ";
$nachname
= <STDIN>;
print
"Ihr Name lautet: $vorname $nachname";
________________________________________________
An
diesem Programm sind zwei Dinge erläuterungsbedürftig. Der Befehl
"chop" entfernt das letzte Zeichen der Variablen $vorname. Warum das? Weil das
letzte Zeichen der Benutzereingabe immer das RETURN-Zeichen ist, mit dem die
Eingabe abgeschlossen wird. Damit dieses Zeichen aber nicht ausgegeben wird -
Vor- und Nachname würden dann in zwei Zeilen stehen - muß es
entfernt werden.
Ein
weiterer Punkt ist der Umstand, daß die Angabe, was mit 'print' auf dem
Bildschirm ausgegeben werden soll, in Anführungszeichen steht. Allerdings
erkennt Perl, daß es sich bei $vorname und $nachname um Bezeichner
für Variablen handelt und setzt den Wert der Variablen ein. Will man das
nicht, soll also die Ausgabe auf dem Bildschirm tatsächlich die
Zeichenkette "Ihr Name lautet: $vorname $nachname" sein, dann muß man
entweder die Zeichenkette in einfache Anführungszeichen setzen oder einen
Backslash vor die Variablenbezeichner stellen:
print
'Ihr Name lautet: $vorname $nachname';
print
"Ihr Name lautet: \$vorname \$nachname";
Benutzereingabe
beim Programmaufruf
Eine
Benutzereingabe, die gleich beim Aufruf des Programm angegeben wird, wird in
einer festgelegten internen Variablen gespeichert und kann somit einfach
verwendet werden. Die interne Variable ist jedoch nicht eine skalare Variable,
sondern ein sogenannter Array (Liste). Wir interessieren uns hier aber nur
für den ersten Eintrag in dieser Liste - und der ist wiederum eine skalare
Variable, die aber etwas anders angesprochen wird: $ARGV[0]. Der zweite Eintrag
in der Liste wird mit $ARGV[1] angesprochen usw.
Bsp.:
________________________________________________
$name
= $ARGV[0];
print
"Hallo " . $name;
________________________________________________
Dieses
Programm könnte name.pl heißen. Es würde dann z.B. so
aufgerufen werden:
perl
name.pl Goethe
Die
Ausgabe wäre dann
Hallo
Goethe
Schleifen,
Bedingungen, Iteratoren
Die
drei Anweisungen für Schleifen, Bedingungen und Iteratoren dienen zur
Wiederholung oder zur bedingten Ausführung eines Programmteils.
Bedingungen
Alle
drei verwenden Bedingungen. Eine Bedingung hat einen Wahrheitswert (wahr,
falsch). Zumeist werden diese Bedingungen mit Vergleichsoperatoren geschrieben:
Für
Zahlen:
==
|
gleich
|
!=
|
ungleich
|
>
|
größer
als
|
<
|
kleiner
als
|
>=
|
größer
gleich als
|
<=
|
kleiner
gleich als
|
Für
Zeichenketten:
eq
|
gleich
|
ne
|
ungleich
|
>
|
größer
als*
|
<
|
|
Bsp:
3
== 3
(wahr)
3
== 4
(falsch)
3
!= 4
(wahr)
$i
= 1;
$i
< 3
(wahr)
$z
= "Hallo";
$z
eq "hallo"
(falsch)
Diese
Bedingungen können nun in den drei Anweisungen verwendet werden.
Bedingte
Verzweigung mit 'if'
Bedingungsabfrage:
if
(bedingung ist wahr) {
Programmblock
I
}
elsif (2. bedingung ist wahr) {
Programmblock
II
.....
}
else {
Programmblock
III
}
Der
Programmblock I wird nur ausgeführt, wenn die 1. Bedingung wahr ist. Der
Programmblock II wird nur ausgeführt, wenn die 2. Bedingung wahr ist usw.
(beliebig viele Ergänzungen sind möglich). Wenn alle Bedingungen
falsch sind, dann wird der Programmblock III ausgeführt.
Wie
die Beispiele zeigen, sind kürzere Fassungen möglich.
_______________________________________________
$i
= 3;
if
($i<5) {
print
"hallo";
}
_______________________________________________
$passwort
= "goethe";
if
($passwort eq "goethe") {
print
"Sie sind eingeloggt.";
}
else {
print
"Falsches Passwort.";
}
_______________________________________________
$passwort
= "goethe";
if
($passwort eq "goethe") {
print
"Sie sind eingeloggt.";
}
elsif ($passwort eq "") {
print
"Sie müssen ein Passwort eingeben.";
}
else {
print
"Falsches Passwort.";
}
_______________________________________________
Schleife
mit 'while'
Schleife:
while. Solange die Bedingung wahr ist, wird der Programmblock ausgeführt:
while
(bedingung ist wahr) {
Programmblock
}
Bsp.:
$i
= 1; $z = 5;
while
($i != $z) {
$i
= $i+1; # kann auch einfach $z++; geschrieben werden
print
"hallo ";
}
ACHTUNG:
Ein beliebter Programmierfehler ist das Erzeugen einer endlosen Schleife. Wenn
die Bedingung niemals falsch wird, dann läuft die Schleife endlos weiter.
Schleife
mit 'for'
Kürzer
läßt sich eine Schleife, die Variablen schrittweise verkleinert oder
vergrößert mit 'for' formulieren.
for
(startwert; bedingung; inkrement) {
Programmblock
}
Bsp.:
for
($i = 0; $i < 5; $i++) {
print
"hallo ";
}
Öffnen
einer Datei
Um
eine Datei zu öffnen, muß der Name der Datei einer sogenannten
file
handle
zugewiesen werden. Die Anweisung dafür lautet “open”. Bsp.:
open
(INPUT, "text.txt");
Anschließend
kann man mit der
file
handle
,
deren Name frei wählbar ist, auf den Inhalt der Datei zugreifen. Der
nachstehende Befehlt liest eine Zeile aus der Datei:
$zeile
= <INPUT>;
Der
oben angeführte Befehl öffnet die Datei zum Lesen. Mit dem Zusatz
eines Zeichens (>) wird die Datei zum Schreiben geöffnet:
open
(INPUT, ">text.txt"); #besser verständlich:
open
(OUTPUT, ">text.txt");
ACHTUNG!
Mit diesem Befehl wird die Datei neu angelegt, d.h. eine evtl. bestehende Datei
dieses Namens wird gelöscht. Will man an eine bestehende Datei etwas
anfügen, lautet der Befehl dafür:
open
(OUTPUT, ">>text.txt");
Da
es beim Öffnen der Datei aus vielen Gründen zu Problemen kommen kann,
wird der Befehl zum Öffnen der Datei normalerweise mit einer Kondition
verbunden:
open
(INPUT, "text.txt") || die "Probleme beim Öffnen von text.txt");
Die
beiden Striche in der Mitte sind ODER-Zeichen. Wenn die Rückgabe des
ersten Befehls negativ ist, dann wird der zweite Befehl ausgeführt. Dieser
Befehl beendet das Programm und zeigt dabei den Text auf dem Bildschirm an.
Ein-
und Ausgabe einer Datei
Zuerst
muß die Datei geöffnet werden.
Zur
Eingabe kann man normalerweise so vorgehen:
while
($input = <INPUT>) {
tu
was;
}
Das
Programm liest solange eine Zeile aus der Datei, wie dort Zeilen sind. Im
while-Block kann man mit der Zeile, die in der Variablen $input gespeichert
ist, etwas tun. Der
Ausdruck ($input = <INPUT>) gibt solange den Wert 'wahr' zurück,
wie der Variablen $input ein neuer Wert aus der Datei zugewiesen werden kann.
Ausgabe:
print
OUTPUT "und tschüß";
Schreibt
den Text in die angegebene Datei.
Ausf.
Beispiel
________________________________________________
#Dieses
Programm liest den Text aus text.txt und
#fügt
vor jeder Zeile das Wort 'Text' ein. Ausgabe
#in
textneu.txt
open
(INPUT, "text.txt");
open
(OUTPUT, ">textneu.txt");
while
($input = <INPUT>) {
print
OUTPUT "Text: $input";
}
________________________________________________
Eine
Standardprogrammform sieht so aus:
________________________________________________
open
(INPUT, "$ARGV[0]") || die "Kann $ARGV[0] nicht öffnen" ;
open
(OUTPUT, ">$ARGV[1]") || die "Kann $ARGV[1] nicht anlegen";
while
($input = <INPUT>) {
tu
was;
}
________________________________________________
Wenn
dieses Programm 'ersetze.pl' heißt, dann wird es so aufgerufen:
perl
ersetze.pl text.txt textneu.txt
Suchen
und Ersetzen
Sucht
nach 'vorher':
$zeile
=~ m/vorher/;
Meist
in dieser Weise verwendet:
if
($zeile =~ m/vorher/) {
tu
was;
}
Wenn
die Zeile 'vorher' enthält, dann tu was.
Ersetzt
in $zeile alle Vorkommen von 'vorher' gegen 'nachher'.
$zeile
=~ s/vorher/nachher/g;
Man
kann auch Sonderzeichen verwenden:
\n
Zeilenwechselzeichen (Returnzeichen)
\t
Tabulator
\d
Zahlen
^
Zeilenanfang
$
Zeilenende
Diese
Sonderzeichen können teilweise (\n \t) auch bei der Ausgabe verwendet
werden.
Gesamtbeispiel:
_______________________________________________
#Öffnet
Datei, liest zeilen ein und tauscht in ihnen
#jedes
Vorkommen von Ich am Anfang der Zeile
#gegen
Wir
open
(INPUT, "$ARGV[0]") || die "Kann $ARGV[0] nicht öffnen" ;
open
(OUTPUT, ">$ARGV[1]") || die "Kann $ARGV[1] nicht anlegen";
while
($input = <INPUT>) {
$input
=~ s/^Ich/Wir/g;
print
OUTPUT $input;
}
________________________________________________
Mehrere
Zeichen wechseln:
$input
=~ tr/AEIOU/aeiou/;
Zeichenfunktionen
Perl
stellt eine Reihe von Funktionen zur Verfügung, die für die
Manipulation von Zeichenketten gedacht sind.
chop
($zeichen):
|
entfernt
das letzte Zeichen von $zeichen
|
length
($zeichen):
|
gibt
die Länge (Anzahl der Zeichen) von $zeichen zurück
|
lc
($zeichen):
|
gibt
$zeichen in Kleinbuchstaben zurück
|
uc
($zeichen):
|
gibt
$zeichen in Großbuchstaben zurück
|
lcfirst
($zeichen):
|
gibt
$zeichen zurück, erster Buchstabe kleingeschrieben
|
ucfirst
($zeichen):
|
gibt
$zeichen zurück, erster Buchstabe großgeschrieben
|
index
($zeichen, $substring):
|
Gibt
die Position von $substring in $zeichen zurück. Wenn nicht vorhanden, dann
Rückgabewert -1
|
rindex
($zeichen, $substring):
|
Wie
index, nur beginnt die Suche von rechts
|
substr
($zeichen, $offset, $laenge):
|
Extrahiert
aus $zeichen an der Stelle $offset einen String der Länge $laenge. Wenn
$laenge weggelassen wird, dann Extraktion bis zum Ende von $zeichen
|
Bsp.:
$input
= "Dies ist ein Satz. Er ist kurz.";
$erg
= uc ($input); # $erg: "DIES IST EIN SATZ...
$erg
= lc ($input); # $erg: "dies ist ein satz...
$erg
= length ($zeichen); # $erg = 31
$erg
= index ($input, "ist");# $erg = 5, da das erste 'ist' gefunden wird
$erg
= rindex ($input, "ist");# $erg = 22. Das zweite 'ist' wird gefunden
$erg
= substr ($input,5,13); # $erg = "ist ein Satz. "
$erg
= substr ($input,5); # $erg = "ist ein Satz. Er ist kurz."
Reguläre
Ausdrücke
Computer
kodieren Buchstaben, Zahlen, Satzzeichen, Zeilenwechsel, Tabulatoren jeweils
mit einem eigenen Zeichen, Zusammensetzungen solcher Zeichen bilden
Zeichenketten (
strings).
Reguläre Ausdrücke dienen zur Beschreibung von Zeichenkettenmustern.
Man kann mit ihnen Klassen von Zeichenketten beschreiben.
Zur
Beschreibung der Klassen werden Metazeichen verwendet. Soll das Metazeichen als
einfaches Zeichen einer Zeichenkette verwendet werden, wird ein '\'
vorangestellt. Einfachstes Metazeichen ist der Punkt. Er steht für ein
beliebiges Zeichen außer dem newline-Zeichen ( \n).
________________________________________________
#Programmhülle
für alle weiteren Beispiele
$input
= "1 Ich bin's. 2. Ich bins. Daß könntest Du mir glauben! 3)
(Oder?)\n";
#Wenn
der Text in $input eine Teilkette enthält, die dem reg. Ausdruck
#
genügt, dann zeige diese Teilkette an
if
($input =~ m/(b.n)/) { #Nur noch der Text in Klammern muß
print
"Treffer: ". $1; # im folgenden ersetzt werden
}
else {
print
"Kein Treffer";
}
_______________________________________________
Beliebiger
Buchstabe: \w
Bsp.:
m/(D\w)/
PROBLEM:
Paßt nicht auf Umlaute u ß
Beliebige
Zahl: \d
Bsp.:
m/(\d )/
Erg.: '1 '
Bsp.:
m/(\d\.)/
Erg.: '2.' Der '\' macht aus dem Metazeichen '.' einen einfachen Punkt.
Anfang
der Zeile: ^
Bsp.:
m/(^\d)/
Erg.: '1'. Paßt nur auf die 1
Ende
der Zeile: $
Bsp.:
m/(\n$)/
Erg.: '1'.
Gruppe:
[ ]
Gruppen
können einfache Listen sein:
Bsp.:
m/(D[aeou])/
Erg.: 'Da' (paßt auch auf 'Du')
Oder
Mengen enthalten:
[a-z]
: paßt auf jeden kleinen Buchstaben. Im Dt. so zu ergänzen:
[a-zäöüß]
[0-9]:
paßt auf jede Ziffer
[1-5]:
paßt auf jede Ziffer von 1 bis 5
[A-ZÄÖÜ]
paßt auf alle Großbuchstaben
[A-zÄÖÜäöüß]
paßt auf jeden Buchstaben. Vergleichbar mir \w
Quantifikatoren:
?
ein oder kein Vorkommnis des voranstehenden Zeichens
+
ein oder beliebig viele Vorkommnisse des voranstehenden Zeichens
*
null oder beliebig viele Vorkommnisse des voranstehenden Zeichens
{n;m}
paßt wenn es mindestens n mal und höchstens m mal vorkommt
Bsp.:
(bin'?s)
Erg.: "bin's" (Würde auch auf 'bins' passen)
Bsp:
(k.+t)
Erg.: 'könntest'
Problem
der "greediness", der 'Gier' reg. Ausdrücke. Wenn man z.B. alle Zeichen
zwischen 'I' und 'n' erfassen möchte (als Treffer wäre "Ich bin"
angezielt), dann würde man das so formulieren:
(I.+n)
Das
tatsächliche Ergebnis ist aber:
"Ich
bin's. 2. Ich bins. Daß könntest Du mir glauben"
Will
man das die Suche nach dem ersten'n' beendet wird, muß man ein '?'
verwenden:
(I.+?n)
Erg.:
Ich bin
Rückbezüge
(Backreferences)
Man
kann Reg. Ausdrücke auch für Ersetzefunktionen verwenden. Allerdings
kann man in solchen Fällen zumeist nicht angeben, wie denn das gefundene
Muster lautet. Wenn man es dennoch für einen Rückbezug braucht, dann
setzt man den Tel des reg. Ausdrucks, der weiter verwendet werden soll in
Klammern. Man kann sich dann mit $1 usw auf diese Muster beziehen.
Bsp.:
________________________________________________
#Hebt
alle strings wie 'bin', 'ben' mit doppelten Strichen hervor:
$input
= "1 Ich bin's. 2. Ich bins. Daß könntest Du mir glauben! 3)
(Oder?)\n";
$input
=~ s|(b.n)|--$1--|g;
print
$input;
________________________________________________
________________________________________________
#sortiert
Datenbankausgabe in übliche Folge:
$input
= "Nachname: Klein Vorname: Uta\n";
$input
=~ s|Nachname: (\w+) Vorname: (\w+)\n|$2 $1|;
print
$input;
________________________________________________
Dieser
erste Abschnitt sollte soviel Wissen vermitteln, daß man eine Datei
zeilenweise einlesen, den Inhalt der Zeile mit String-Funktionen oder
regulären Ausdrücken manipulieren und das Ergebnis in eine neue Datei
speichern kann.
Ein
weiterer Abschnitt, der beschreibt, wie man aus Texten Wortlisten erstellt und
sortiert, soll folgen. Wie immer sind Anregungen, Kritik und Wünsche, was
noch behandelt werden soll, sehr willkommen.
ist
fortzusetzen ...
Fotis Jannidis
Veröffentlicht am 13.2.1999
Anhang.
Den
Perl-Interpreter kann man für alle möglichen Plattformen und als
Sourcecode (in C) hier finden: http://www.perl.com
Die
Windows 32 Version findet man hier:
http://www.ActiveState.com/ActivePerl
Einen
sehr guten und robusten ASCII-Editor, den Programmer's File Editor, der auch
noch kostenlos ist, findet man unter:
http://www.lancs.ac.uk/people/cpaap/pfe/
[*]
Bezieht sich auf die Position in der ASCII-Tabelle. (Achtung wg. der dt.
Sonderzeichen. Ihre Position in der ASCII-Tabelle führt zu eigenartigen
Ergebnissen, z.B. ist ö größer als z.