====== Handle souboru v Perlu ======
Handle souboru nemá ustálený český překlad. Někdy se používá "deskriptor souboru", což není úplně totéž a není to o nic češtější. Má se tím na mysli identifikátor, kterým v programu odkazujeme na již otevřený soubor, resp. vstupní nebo výstupní proud (nemusí jít nutně o pojmenovaný soubor na disku). V každém programu jsou na začátku k dispozici tři již otevřené handly: STDIN (standardní vstup), STDOUT (standardní výstup) a STDERR (standardní chybový výstup).
V Perlu je zacházení s handly lehce problematické, protože handly nezapadají dobře do systému datových typů. Potíže ovšem nastanou až při pokusu o složitější operace, např. o předání handlu jako parametru do funkce. Běžné použití handlů je naopak velmi jednoduché:
# Handly "VSTUP" a "VYSTUP"
open(VSTUP, 'soubor1.txt') or die("Nelze číst soubor1.txt: $!\n");
open(VYSTUP, ">soubor2.txt") or die("Nelze psát soubor2.txt: $!\n");
while()
{
my $radek = $_;
print VYSTUP ($radek);
}
close(VSTUP);
close(VYSTUP);
Závorky u funkcí ''open()'', ''print()'' a ''close()'' jsou důsledkem mé dávné céčkařské minulosti a zde jsou zcela nadbytečné. Všimněte si však čárek. Zatímco u funkce ''open()'' je za handlem ''VSTUP'' čárka, takže handle vstupuje do funkce jako parametr, u funkce ''print()'' se čárka za ''VYSTUP'' nedává, aby se dalo najevo, že tím označujeme otevřený výstupní proud (místo výchozího ''STDOUT''), a že tedy nejde o jeden z objektů, které se mají vypsat.
Konvence velí, že v handlu jsou všechna písmena velká; z hlediska syntaxe Perlu jsou ovšem malá písmena také v pořádku. Ve výše uvedeném příkladu bychom tedy mohli napsat ''open(vstup, 'soubor1.txt')'' a dokonce bychom mohli mít v jednom programu dva handly, které se liší jen velkými písmeny, např. ''HANDLE'' a ''handle''.
Handle funguje trochu jako pátý datový typ: vedle skalárů (např. ''$h''), polí (např. ''@h''), hashů (např. ''%h'') a funkcí (např. ''&h'') ještě existují handly (např. ''h''). Nejde ovšem o plnohodnotný datový typ a kvůli absenci rozlišovacího znaku s handly nejde zacházet stejně jako s ostatními typy. Existuje sice rozlišovací znak hvězdička, avšak ''*h'' z nějakého záhadného důvodu označuje tzv. //typeglob,// tj. celou skupinu pěti věcí, sdílejících identifikátor ''h'': skaláru, pole, hashe, funkce a handlu. Viz též manuálovou stránku perldata, část //Typeglobs and Filehandles//.
A není jednodušší používat tohle?
open $HANDLE,' --- //[[stepanek@ufal.mff.cuni.cz|stepanek]] 18.3.2010 17:58//
===== Jak předat handle jako parametr do funkce =====
Existující handle můžeme podle manuálu uložit do proměnné takto jako //symbolický odkaz://
$fh = *STDOUT
nebo takto jako //skutečný odkaz://
$fh = \*STDOUT
Handle, který takto uložíme do proměnné, pak můžeme třeba předat jako parametr do funkce. Vypíšeme-li obsah proměnné ''$fh'', v prvním případě uvidíme ''*main::STDOUT'', ve druhém pak ''GLOB(0x191a07c)''.
Pozor, i handle je součástí tabulky symbolů daného modulu/balíčku. Pokud jsme tedy např. v modulu MojeHandly vyrobili handle MUJVSTUP, z ostatních modulů ho uvidíme jako MojeHandly::MUJVSTUP.
Viz též perlsub, část //Passing Symbol Table Entries (typeglobs)//.
Příklad předání handlu do funkce (použití ve funkci je vždy stejné, ať už jsme handle předali jako symbolický, nebo jako skutečný odkaz:
open(VYSTUP, ">soubor2.txt") or die("Nelze psat soubor2.txt: $!\n");
$fh = *VYSTUP;
funkce($fh);
close(VYSTUP);
sub funkce
{
my $handle = shift;
print STDERR ("Parametr handle = $handle\n");
print $handle ("Toto je vystup do souboru.");
}
===== Jak vrátit handle jako výsledek funkce =====
Glob můžeme také využít k tomu, že deklarujeme handle jako lokální v nějaké funkci. Potom ho můžeme z této funkce vrátit jako výsledek (viz perldata, //Typeglobs and Filehandles//).
sub newopen {
my $path = shift;
local *FH; # not my!
open (FH, $path) or return undef;
return *FH;
}
$fh = newopen('/etc/passwd');
Tohle (lokální handle) už však prý není nutné, protože všechny funkce, které vyrábějí handly (např. ''open()'') automaticky vytvoří anonymní handle, jestliže místo handlu dostanou //neinicializovanou// skalární proměnnou. Můžeme napsat třeba ''open(my $fh, ...)'' nebo ''open(local $fh, ...)''. Takto vzniklý handle zanikne na konci bloku, pokud už na ně nevede žádný odkaz. Můžeme je tedy vrátit jako výsledek funkce: to je ten odkaz, který zajistí jejich přežití, zatímco jejich původně lokální platnost zaručí, že se nám nebudou tlouct s jinými globálními handly. Příklad:
sub myopen {
open my $fh, "@_"
or die "Can't open '@_': $!";
return $fh;
}
{
my $f = myopen(";
# $f implicitly closed here
}
Takto vyrobený a vrácený handle je skutečný, nikoli symbolický odkaz, takže se nemusíme starat o to, ve kterém modulu a jmenném prostoru byl handle vyroben. Když si ho necháme vypsat, dostaneme např. ''GLOB(0x2ba214)''.
===== Jak otevřít předem neznámý počet souborů =====
Výtah z nápovědy k funkci ''open()'' na manuálové stránce perlfunc:
**open FILEHANDLE,EXPR**
Výsledkem EXPR je cesta k souboru. Místo FILEHANDLE může být také výraz (např. skalární proměnná), v tom případě se jeho výsledek považuje za skutečný název handlu (symbolický odkaz).
Pokud tedy chceme otevírat celou řadu souborů a dopředu nevíme, kolik jich bude (tj. potřebujeme něco jako pole handlů), můžeme použít skalární proměnné nebo pole, ale nejdřív do nich musíme vygenerovat skutečné (a jedinečné) názvy handlů, jako v tomto příkladu z perlfunc:
# process argument list of files along with any includes
foreach $file (@ARGV) {
process($file, 'fh00');
}
sub process {
my($filename, $input) = @_;
$input++; # this is a string increment
unless (open($input, $filename)) {
print STDERR "Can't open $filename: $!\n";
return;
}
local $_;
while (<$input>) { # note use of indirection
if (/^#include "(.*)"/) {
process($1, $input);
next;
}
#... # whatever
}
}
Viz také manuálovou stránku perlopentut.