Was Ist ein Lexikalischer Filehandle?

Inhalt:

Klassische Filehandles mit 'barewords'

Die gängigste Variante in Perl, Dateien zu öffnen, ist mit bareword-Filehandles, d.h. normalerweise sind es großgeschriebene namen wie FH, FILEHANDLE etc. Diese sind dann von überall aus sichtbar und müssen explizit geschlossen werden, sonst bleiben sie bis zum Ende des Skripts offen.

open FH, '<', 'file' or die $!;
print while <FH>;
close FH;

Lexikalische Filehandles

Alternativ benutzt man lexikalische Filehandles, das sind dann im Prinzip ganz normale Variablen mit my() deklariert.

open my $fh, '<', 'file' or die $!;
print while <$fh>;
close $fh;

Ein großer Vorteil ist, dass diese Variablen unter UseStrict derselben Kontrolle unterliegen wie alle anderen Variablen. Zudem lassen sie sich gut kapseln. Ein Beispiel, um eine Datei im 'slurp-Modus' auszulesen, d.h. komplett und nicht nach Zeilen getrennt:

my $data = do { open my $fh, '<', 'file' or die $!; local $/; <$fh> };

open my $fh... öffnet die Datei im Filehandle $fh, wobei $fh nun nur im =do=-Block gültig ist. Dadurch erübrigt sich praktischerweise das Schließen des Filehandles, da beim ungültigwerden eines Filehandles die Datei automatisch geschlossen wird. Wenn close fehlschlagen könnte (das ist der Fall, wenn man das Filehandle zum Schreiben benutzt hat), sollte man aber trotzdem unbedingt explizit close aufrufen, um an den Rückgabewert zu kommen und eine richtige Fehlerbehandlung machen zu können.

local $/ setzt die Variable $/ (perldoc perlvar) lokal in diesem Block auf undef, wodurch beim Einlesen einer Datei alles auf einmal gelesen wird. <$fh> schließlich liest die Datei ein und gibt den Wert an die aufrufende Variable des do zurück, also $data.

Das mag zuerst etwas kryptisch aussehen, ist aber eine gängige Form.

Lexikalische Filehandles als Objekte

Lexikalische Filehandles werden, wenn sie über open implizit erzeugt werden, in die Klasse IO::Handle geblesst. Diese Klasse bietet einige nützliche Methoden, die man allerdings mit "use IO::Handle;" erst laden muss. So lassen sich alle gängigen Operationen (wie print, close, ...) auch als Methoden schreiben:

use IO::Handle;

open my $fh, ">", "output" or die $!;
$fh->print("blah\n") or die $!;
$fh->close or die $!;

Die beliebte Klasse IO::Socket::INET, die die meisten Leute und Module benutzen, um TCP-Verbindungen zu erzeugen, erbt übrigens von IO::Handle. Es gibt auch IO::File und IO::Dir, die auch von IO::Handle erben. Sie bieten Methoden, die speziell für Dateien bzw. Verzeichnisse gedacht sind. Es lohnt sich, die Dokumentation einmal anzusehen.

Lexikalische Filehandles in Datenstrukturen

Lexikalische Filehandles lassen sich auch bequem in Arrays und Hashes unterbringen:

# öffne fünf Dateien zum Einlesen
my @filehandles;
for my $i (1 .. 5) {
    open $filehandles[$i], '<', "data$i.dat" or die $!;
}

Man stößt aber auf ein Problem, wenn man ein solches Filehandle in einer print Anweisung nutzen möchte:

print $filehandles[$i] "Hello!";        # FEHLER!
Perl erkennt hier den ersten Parameter des print nicht als Filehandle, sondern als auszugebenden Wert, und beklagt sich folgerichtig über den fehlenden Operator hinter der eckigen Klammer. Die Lösung hier ist, einen Block zu benutzen, der als Wert das Filehandle liefert:
print { $filehandles[$i] } "Hello!";    # funktioniert!
Lesbarer ist hierbei die oben besprochene Pfeilschreibweise:
use IO::Handle;
$filehandles[$i]->print("Hello!");      # funktioniert auch!

Filehandles in Closures

Man hat z.B. ein Modul und zwei Methoden, die auf ein und denselben Filehandle zugreifen müssen. Man kann dies sehr schön mit lexikalischen Filehandles kapseln, indem man die beiden Methoden in einen eigenen Scope packt:

{
  my $fh;
  use Fcntl qw(:flock); # import LOCK_EX
  sub create_lock {
    open my $fh, '+<', 'lockfile' or die $!;
    flock $fh, LOCK_EX;
  }

  sub unlock {
    undef $fh;
  }
}

$fh ist hier nur im angegebenen Block gültig, kann aber trotzdem über zwei Subroutinen hinweg verwendet werden. Für weitere Infos zu Closures siehe Perldoc.perlsub und Perldoc.perlref

opendir

Das ganze geht natürlich auch mit opendir:

opendir my $dh, 'directory' or die $!;
while (my $file = readdir $dh) {
  my $size = -s "directory/$file";
  ...
}

-- TinaMueller, HaraldBongartz, ChristophBussenius - 10 Nov 2005

UtilFaqSubForm edit

Titel Was Ist ein Lexikalischer FileHandle??
Autor TinaMueller
Bereich FaqEinUndAusgabe
Topic revision: r4 - 23 Jan 2007 - 12:52:00 - ChristophBussenius
 
Bitte die NutzungsBedingungen beachten.
Bei Vorschlägen, Anfragen oder Problemen mit dem PerlCommunityWiki bitten wir um Rückmeldung.