====== 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.