Table of Contents
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(<VSTUP>) { 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,'<vstup1.txt';
— 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("</etc/motd"); print <$f>; # $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.