Mit Perl 5.6.0, das seit einiger Zeit auf www.perl.com bereit steht,
zogen einige interessante Neuerungen in den Perlkern und die beiliegenden
Module ein.
Während [1] oder [2] einen Überblick über die wichtigsten Änderungen bieten, soll es heute um die praktische Anwendung einiger ausgesuchter neuer Gimmicks gehen.
Wer nach einem Treffer eines regulären Ausdrucks wissen will,
welche Stellen des untersuchten Strings zum Erfolg führten,
dem kommen die neuen Arrays @- und @+ gerade recht.
Gerüchten zufolge wird nun die Perl-Entwicklung eingestellt,
da keinerlei Sonderzeichen mehr für die Namen von Spezialvariablen
zur Verfügung stehen ([3]).
Das erste Element von @- schreibt sich traditionsgemäß als $-[0]
und gibt die Indexposition des Teilstrings an, auf den der reguläre
Ausdruck ansprach. Das erste Element von @+ zeigt auf die Indexposition
kurz nach dem Ende des Treffers:
if( "123" =~ /\d/ ) {
print "Match $-[0]..$+[0]", "\n";
}
Der reguläre Ausdruck /\d/ passte schon auf die erste Ziffer von
"123", und dementsprechend fällt das Ergebnis aus:
Match 0..1
An Indexposition 0 (oder auch: Offset 0 ) steht die Ziffer 1, die den
Treffer auslöste. Das Ende des Treffers ist direkt dahinter, da
der reguläre Ausdruck nur eine einzelne Zahl suchte: Deswegen steht
$+[0] auf 1. Hier ein Ausdruck, der sich mit dem Modifizierer
/g Stück für Stück durch einen String hangelt:
my $string = "Abc, die Katze liegt im Schnee.";
# 0123456789012345678901234567890
while( $string =~ /\w+/g ) {
printf "%-6s (%2d .. %2d)\n",
$&, $-[0], $+[0];
}
Er sucht mit \w+ zusammenhängende Wörter und gibt Aufschluss über die
Einschlagstellen:
Abc ( 0 .. 3)
die ( 5 .. 8)
Katze ( 9 .. 14)
liegt (15 .. 20)
im (21 .. 23)
Schnee (24 .. 30)
Das Beispiel-Snippet verwendet die Spezial-Perl-Variable $&, die nach
einem Match den gematchten String enthält. Wie in [5] erläutert, hat
dies aber Performanceeinbußen zur Folge. Ginge es um Geschwindigkeit,
könnten wir dies mit unseren neuerlangten Kenntnissen aber auch mit
substr( $string, $-[0], $+[0] - $-[0] ) ausdrücken, denn die Länge
des Treffers steht mit $+[0] - $-[0] fest.
Nun sind @- und @+ Arrays und bieten demnach Raum für
weitere Informationen.
Sie speichern nicht nur die Trefferdaten,
sondern auch die Positionen der definierten Untergruppen in dem
verwendeten regulären Ausdruck:
$string = "012";
if( $string =~ /(\d)(\d)(\d)/ ) {
for $i (0..$#-) {
print "$i: $-[$i]..$+[$i]\n";
}
}
Die Längen der Arrays @- und @+ stehen mit $#- und $#+ fest.
Jede der einzelnen (\d)-Gruppen passt auf eine der Ziffern in
"012" und demnach geben die Werte in @- und @+ folgende
Daten bekannt:
0: 0..3
1: 0..1
2: 1..2
3: 2..3
Der Gesamtmatch fand also im Bereich der Indexpositionen 0 bis 2
statt --
die Einträge in @+ zeigen, wie schon erwähnt,
jeweils auf die Position nach dem entsprechenden Match.
Die erste Untergruppe fand sich auf Position <0 >, die
zweite Untergruppe auf 1 und die dritte auf 2. Jede der Untergruppen
ist genau ein Zeichen lang.
Zusätzlich zu my gibt es nun our, um den Gültigkeitsbereich
von Variablen zu definieren. Während my eine lokale
Variable im aktuellen Block, der aktuellen Datei oder in einem
eval-Konstrukt
definiert, legt die Anweisung
our $variable; # Globale Variable $variable
fest, dass $variable sich auf eine globale Variable bezieht, die
im aktuellen Gültigkeitsbereich (Block, Datei, eval) unter
$variable ansprechbar ist. So können sich beispielsweise zwei
Funktionen eine globale Variable teilen, die das Hauptprogramm
gar nicht kennt:
use strict;
funktion();
andere_funktion();
sub funktion {
our $variable = "ABC";
print "$variable\n";
}
sub andere_funktion {
our $variable;
print "$variable\n";
}
Nun bot Perl schon immer globale Variablen an -- lässt man my
einfach weg, dehnt dies den Gültigkeitsbereich schlagartig auf
das gesamte package aus. our stuft da feiner ab: Die Variable
ist zwar global, ansprechen lässt sie sich aber nur im lexikalischen
Scope der our-Deklaration -- im Beispiel oben steht $variable
zwar innerhalb funktion und andere_funktion zur Verfügung, aber
weder eine andere Funktion, die $variable nicht als our deklariert
noch das Hauptprogramm können auf $variable zugreifen.
our funktioniert auch mit der Direktive use strict vars, die
perl sonst meckern lässt, falls globale Variablen zum Einsatz
kommen, ohne dass der jeweilige Package-Name mit erwähnt wird.
Bislang diente die Direktive use vars dazu, globale Variablen
so abzusegnen, dass use strict Ruhe gab -- diese Aufgabe übernimmt
nun our. Und es kann noch mehr: Während use vars an
Package-Grenzen abprallte, springt our auch darüber hinweg, solange
sich der Zirkus in der gleichen Datei abspielt:
package Erstes;
our $variable = "ABC"; # Deklariert $Erstes::variable
package Zweites;
print "$variable\n"; # Greift auf $Erstes::variable zu
Genau wie my erfordert our für die Deklaration mehrerer Variablen
Klammern:
our $variable;
our ( $erste, $zweite );
In der Programmierung mit Modulen kommt our zum Zug, wenn es
um Modul-Variablen mit besonderem Auftrag geht: @ISA, @EXPORT,
$VERSION und Konsorten sind global, für sie kommt our gerade recht.
Das Programm h2xs, das perl beiliegt und mit dem man mit
h2xs -Axn Module
das Gerüst eines neuen Moduls erzeugen kann, schreibt daher
schon our statt dem früher verwendeten use vars:
package Module;
require 5.005_62;
use strict;
use warnings;
require Exporter;
require DynaLoader;
our @ISA = qw(Exporter DynaLoader);
our %EXPORT_TAGS = ( 'all' => [ qw( ) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw( );
our $VERSION = '0.01';
exists für Array-Elemente und Funktionenexists prüft seit neuestem, ob ein Arrayelement initialisiert wurde
oder nicht. Auch wenn ein Element den Wert undef aufweist, existiert es,
denn es gilt als gesetzt:
my @array = (1, undef, 3);
# $array[1] existiert!
print "Existiert!\n" if exists( $array[1] );
Die define-Funktion hingegen prüft, ob ein Arrayelement initialisiert
wurde und einen Wert ungleich undef aufweist:
my @array = (1, undef, 3);
# $array[1] nicht definiert (undef)!
print "Nicht definiert!\n" if ! defined( $array[1] );
Erst ein mit delete zerstörtes Arrayelement veranlasst exists,
einen falschen Wert zurückgeben:
my @array = (1, 2, 3);
delete $array[1];
# $array[1] existiert nicht mehr!
print "Existiert nicht!\n" if ! exists( $array[1] );
Neben Hash- und Arrayelementen kann Perl auch Funktionen daraufhin
überprüfen, ob sie deklariert bzw. definiert wurden. defined liefert
zu einer definierten Funktion einen wahren Wert zurück:
# funktion ist definiert!
print "Definiert!\n" if defined &funktion;
sub funktion { print "Hey!\n"; };
defined &function ruft hierbei die Funktion funktion
nicht auf, sondern sieht lediglich
nach, ob sie definiert wurde, ob also ein Aufruf der Funktion erfolgreich
verliefe. Perl erlaubt es allerdings auch, Funktionen nur
zu deklarieren, ohne auch notwendigerweise zu definieren, was sie
denn genau tun sollen. Diesen Fall prüft exists:
sub funktion;
# funktion ist deklariert (aber nicht definiert)
print "funktion deklariert!\n" if exists &funktion;
Im vorliegenden Fall würde der Aufruf von funktion jedoch zu einem
Laufzeitfehler führen, da vergessen wurde, den Funktionsrumpf zu definieren.
Auch exists ruft eine ihr übergebene Funktion nicht auf, sondern
sieht nur nach, ob diese irgendwo deklariert oder definiert wurde.
defined hätte im obigen Fall übrigens einen falschen Wert geliefert,
da die Funktion keineswegs definiert, sondern nur deklariert wurde.
Im warnungsempfindlichen Modus gibt Perl nützliche Hinweise aus, falls
es meint, typische Fallen im Code zu entdecken. Mit dem Schalter
-w beim perl-Aufruf oder auch mit der Spezial-Variable $^W liessen
sich Warnungen auch bisher schon ein- oder ausschalten. Doch dies war
nur global möglich oder zumindest nur dynamisch ge-scoped mit local $^W.
Lexikalisch begrenzte Warnungseinstellungen, die nur innerhalb des jeweiligen
Blocks gelten, erleichtern es, bestimmte Ausnahmeteile des Codes
gegen Warnungen unempfindlich zu machen. Auch schwappen gesetzte Warnungen
nicht mehr in Nachbarmodule über -- jedes kann sein eigenes Warnungsverhalten
definieren.
Perl unterscheidet zwischen Compiletime- und Runtime-Warnungen.
Compiletime-warnungen meldet Perl noch bevor es den Code ausführt.
Manipulationen an $^W für diese Nachrichten mussten deshalb
bisher in einem <BEGIN>-Block vorgenommen werden.
Das neue Pragma
use warnings;
schaltet sowohl Compile- wie auch Runtime-Warnungen auf Blockebene ein
und ersetzt den Schalter -w.
Im Gegensatz dazu nimmt
no warnings;
alles wieder zurück und unterdrückt die Nachrichten im lokalen Block wieder.
Dabei sind Perls Warnungen in Kategorien unterteilt, die sich
jeweils einzeln ein- oder ausschalten lassen. Die Top-Kategorie
all enthält alle in Tabelle 1 aufgelisteten Unterkategorien, die
wiederum teilweise weitere Unterkategorien enthalten.
So bezieht sich beispielsweise die Kategorie io auf alle Warnungen,
die mit I/O-Operationen zusammenhängen. Die Unterkategorie
closed hingegen zielt nur auf diejenigen Warnungen der io-Kategorie,
die mit geschlossenen Filehandles in Verbindung stehen.
chmod
closure
exiting
glob
io
closed/exec/newline/pipe/unopened
misc
numeric
once
overflow
pack
portable
recursion
redefine
regexp
severe
debugging/inplace/internal/malloc
signal
substr
syntax
ambiguous/bareword/deprecated/parenthesis/
precendence/printf/prototype/qw/reserved/semicolon
taint
umask
uninitialized
unpack
untie
utf8
void
y2k
Das Kommando perldoc perldiag bringt Perls Fehlermeldungen und
Warnungen mitsamt schlüssiger Erklärungen und deren Kategorisierung
zutage. Wie man weiss, gibt perl diese ausführlichen Meldungen aus,
falls use diagnostics eingeschaltet ist.
Zur Nachricht print on closed filehandle, die perl meldet, falls
print Daten auf ein bereits geschlossenes Filehandle ausgibt,
steht in perldoc diagnostics beispielsweise:
print() on closed filehandle %s
(W closed) The filehandle you're printing on got itself
closed sometime before now. Check your logic flow.
Hier gibt (W closed) an, dass der Text zu einer
Warnung der Kategorie closed gehört. Die in perldoc perllexwarn
dargestellte Hierarchie weist closed der Kategorie io zu, die
wiederum Teil der Gesamtheit aller Warnungen, der Kategorie all, ist.
Ein anderes Beispiel: Wer sein perl vor dem Übersetzen mit
./Configure -Accflags=-DPERL_Y2KWARN
konfiguriert hat, bekommt seit neuestem Warnungen ins Haus, falls sich im Code potentielle Jahr-2000-Fehler eingeschlichen haben. Steht irgendwo
$string = "19" . $wert;
schreit perl bei aktiviertem use warnings entsetzt auf:
Possible Y2K bug: about to append an integer to '19' at ./t.pl line 6.
Handelt es sich oben aber nicht um eine Kalenderrechnungsfehler, sondern
um beabsichtigten Code, unterdrückt ein no warnings die unerwünschte
Y2K-Warnung im aktuellen Block:
use warnings;
{ no warnings 'y2k';
$string = "19" . $wert;
}
Die Anweisung lässt aber alle übrigen bisher definierten Warnungskategorien
aktiv. Existierte beispielsweise $wert
bislang uninitialisiert, käme perl sogleich mit
Use of uninitialized value in print at t.pl line 6.
daher. Mehrere Kategorien gleichzeitig nimmt das use warnings-Pragma
in Form einer Liste entgegen. Die Anweisung
use warnings qw(once uninitialized);
aktiviert die Warnungen für uninitialisierte (uninitialized)
und nur einmal verwendete Variablen (once).
Von der Kommandozeile aus (perl -w) oder im Shebang
(#!/usr/bin/perl -w) schaltet -w aus Kompatibilitätsgründen
weiterhin alle Warnungen ein, langfristig sollten Skripts aber
use warnings statt dem Schalter verwenden.
Dann springen die Warnungen auch an, falls jemand das Skript
mit perl scriptname startet, obwohl der Warnungsschalter
im Shebang gesetzt war.
Einmal mit -w gesetzte Warnungen kann man übrigens wiederum
mit
no warnings und use warnings im aktuellen Block aus- bzw.
wieder einschalten.
Der Schalter -W hingegen lässt perl die Meldungen auf jeden Fall
ausgeben -- sozusagen der lint der Perl-Welt. Umgekehrt bringt
-X alle Warnungen zum Schweigen, auch die, die explizit angefordert
wurden.
In die vorher erwähnten once-Kategorie fallen Meldungen der Art
Name "main::opt_v" used only once: possible typo at ./once.pl line 12.
Folgendes Beispiel nutzt die getopts-Funktion des Moduls
Getopt::Std, das zu Kommandooptionen wie -x die zugehörige
Variable $opt_x setzt. $opt_x kommt dabei nur einmal zum
Einsatz -- aber das geht in Ordnung und deshalb hilft no warnings 'once'
in handle_options (und nur dort und nicht im Hauptprogramm
oder anderen Funktionen!) die Warnung zu unterdrücken:
use warnings;
use Getopt::Std;
handle_options();
print "Verbose is ", $VERBOSE ? "on" : "off", "\n";
sub handle_options {
no warnings 'once';
getopts('v');
$VERBOSE = $opt_v;
}
Bei den once-Warnungen handelt es sich übrigens um
Warnungen zur Compilezeit -- während die vorher erwähnten
uninitialized-Warnungen zur Laufzeit erfolgen. Beide Varianten
koexistieren friedlich nebeneinander.
Als besonderen Gimmick kann use warnings mit dem FATAL-Parameter
bestimmte Warnungen auch so
eskalieren, dass sie zum Programmabbruch führen:
use warnings FATAL => 'substr';
print substr("abc", 5, 1), "\n";
print "Ende\n";
Hier bricht das Programm in substr mit einer Fehlermeldung ab,
da substr mit Offset 5 weit über den drei Zeichen langen
String hinausgreift.
Die Ausgabe ``Ende'' wird gar nicht mehr erreicht, da perl das Programm
in Zeile 2 bereits abbrach.
Die unter [4] aufgelisteten Manualseiten zeigen detailliert, was man
mit den neuen Warnungen noch anstellen kann.
Doch genug für heute! Nächstes Mal geht's in die vielsprachige
Welt des Unicode, in der perl neuerdings auch kräftig mitmischt.
Viel Erfolg beim Perl-Upgrade -- es lohnt sich!
perldoc perldelta
MikeGTN, ``Perl is finished'',
http://www.segfault.org/story.phtml?mode=2&id=3905b40e-05c0a760
perldoc perllexwarn,
perldoc warnings,
perldoc perldiag
![]() |
Michael Schilliarbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com. |