Práce se soubory

Ovladače

Ovladač souborů slouží pro práci se souborem, ale je možné ho obdobným způsobem použít pro práci s rourou či socketem. Ovladač se vytvoří a přiřadí k souboru pomocí vestavěných funkcí (open, opendir, pipe, sysopen) nebo pomocí funkcí z některých modulů. Konvencí je psát jméno ovladače souboru velkými písmeny (jména ovladačů nemají žádný prefix, proto se takto nebudou plést s rezervovanými slovy). Jména ovladačů patří vždy do tabulky symbolů, není tak možné je např. lokalizovat pomocí my.

Ovladač souboru se uzavře pomocí funkce close, případně closedir.

S ovladačem se pracuje pomocí některých operátorů (operátor < >, operátory testu souborů) nebo pomocí funkcí. V tom případě předáme ovladač jako argument. Řada vestavěných funkcí jako první nepovinný argument očekává jméno ovladače, se kterým má pracovat. Od ostatních argumentů není oddělen čárkou, aby bylo jasné, že se jedná o ovladač. V případě, že jméno ovladače není zadáno, pracuje se s jedním ze standardních ovladačů.

print OVLADAC 1, 2, 3;  # použijeme ovladač OVLADAC
print 1, 2, 3;          # použijeme standardní ovladač

Standardní ovladače

Existují tři ovladače, jejichž jména jsou globální pro celý program a které jsou automaticky otevřeny. Jedná se o ovladač STDIN (standardní vstup), STDOUT (standardní výstup) a STDERR (standardní chybový výstup). Tyto ovladače jsou implicitně používány funkcemi pracujícími s ovladačem v případě, že není uvedeno jeho jméno.

Ovladač _

_ (podtržítko) je speciální ovladač, pomocí kterého je možné opakovaně přistupovat k hodnotám vráceným posledním voláním funkce stat nebo lstat. Při práci s tímto argumentem totiž funkce stat vůbec není volána a pouze vrátí hodnoty, které jsou při každém jejím volání k tomuto ovladači přidruženy. Stejně tak pracuje tento ovladač při použití jako argument operátoru testování souboru.

Zjišťování informací o souboru

Pro zjištění informací o souboru můžeme použít jeden z operátorů tesů souboru. Ty mají tvar -x a zjišťují informace o právech k souboru, o typu souboru či datech modifikace nebo posledního přístupu. Jejich úplný výčet se nachází v kapitole \kap{Operátory}.

Pro další informace je možné použít vestavěnou funkci stat. Ta vrací třináctiprvkový seznam hodnot s vlastnostmi souboru. Informace zahrnují např. vlastníka souboru, čas modifikace, počet odkazů na soubor, práva k soubor, i-node číslo, počet a velikost bloku. V závislosti na operačním systému a souborovém systému nemusejí být některé položky podporovány. Při úspěšném volání funkce je vrácena pravdivá hodnota a zjištěné vlastnosti jsou připojeny k ovladači _.

Pro zjištění informací symbolického odkazu (linku) se používá funkce lstat.

Číslo deskriptoru souboru vrací funkce fileno. Jako argument požaduje jméno ovladače.

print fileno STDIN;  # vytiskne 0
print fileno STDOUT; # vytiskne 1
print fileno STDERR; # vytiskne 2
open DATA, 'data';
print fileno DATA;   # vytiskne 3

Uzavření souboru

Po ukončení práce se souborem, rourou či socketem je dobré spojejí uzavřít. Děje se tak sice automaticky při ukončení programu nebo při opětovném vytvoření spojení pomocí stejného ovladače, ale průběh a následky takového uzavření spojení se od explicitně provedeného trochu liší.

K uzavření souboru použijeme funkci close. Ta provede vyprázdnění vyrovnávací paměti vyhrazené pro spojení, provede případný zápis těchto dat do souboru, soubor uzavře a uvolní tak deskriptor souboru. Podle návratové hodnoty funkce je možné zjistit, jak tyto operace proběhly. V případě úspěchu je vrácena pravdivá hodnota.

Při explicitním uzavření souboru pomocí funkce close doje rovněž k vynulování počítadla řádku ($.). Při automatickém uzavření souboru při opětovném otevření ovladače se tak nestane.

Otevření souboru

Nejčastěji objevující se funkcí používanou pro vytvoření ovladače je funkce open. Je možné ji použít několika způsoby s několika různými argumenty.

Základní použití pro práci se soubory

Jako první argument funkce očekává jméno ovladače souboru. Je-li jako druhý argument zadán řetězec, obsahuje nejčastěji jméno souboru včetně způsobu, jakým by soubor měl být otevřen. Tento způsob se určuje pomocí znaků <, >, >>. Případné bílé znaky kolem jména souboru a symbolu specifikujícího způsob otevření souboru se ignorují.

open FILE, "data.txt";    # otevření pro čtení
open FILE, "< data.txt";  # to samé, explicitně
open FILE, "> data.txt";  # otevření pro přepis
open FILE, ">> data.txt"; # otevření pro připojení na konec souboru

Způsob otevření souboru může být zadán i jako samostatný argument.

open FILE, ">", "data.txt";

Existuje-li globální skalární proměnná, která má stejné jméno jako ovladač, můžeme volat funkci open pouze s prvním argumentem. Jméno souboru potom bude bráno právě z této skalární proměnné (samozřejmě s prefixem $). Neexistuje-li tato proměnná nebo obsahuje-li špatnou hodnotu, otevření souboru se nezadaří a bude vypsáno varování.

$FILE = 'data.txt';
open FILE;

Byl-li soubor otevřený, nejprve se před voláním funkce open uzavře.

Funkce open vrací pravdivou hodnotu v případě, že se soubor podařilo otevřít. V opačném případě vrací hodnotu nepravdivou a nastavuje proměnnou $!. Velmi často se pro testování výsledku otevření souboru a pro ohlášení případné chyby používá konstrukce s operátorem or.

open FILE , "data.txt" or die "Nelze otevřít soubor.";

Návratovou hodnotu funkce open je dobré prověřit, protože i s neúspěšně otevřeným ovladačem je možné pracovat bez toho, aby vnikla chyba (většinou získáme varování). Při čtení z neotevřeného ovladače získáme nedefinovanou hodnotu a funkce eof vrací pravdivou hodnotu. Při zápisu do neotevřeného ovladače dojde ke ztracení zapisovaných dat.

Současné čtení a zápis

Pomocí znaku + před symbolem >, >> nebo < je možné otevřít soubor zároveň pro čtení i pro zápis. Při použití <+ se v případě neexistence souboru zadaného jména nikdy tento soubor nevytvoří, jako je tomu v případě použití > nebo >> (i s případným +).

V případě použití +< je možné do souboru zapisovat kdekoliv (pozice se nastaví pomocí funkce seek) a číst je také možné kdekoliv.

open FILE, "+< data.txt";
$_=<FILE> 
print;     # načteme a vytiskneme první řádek

seek FILE, 5, 0;      # posuneme se na pátý znak
print FILE $data;     # a zapíšeme tam obsah $data

seek FILE, 20, 0;     # posuneme se na dvacátý znak
$_=<FILE>;            # a přečteme zbytek do konce řádku

V případě použití +> dojde vždy k přepsání existujícího souboru nebo k vytvoření nového, když soubor ještě neexistuje. Opět je možné číst i zapisovat komkoliv do souboru.

Použití +>> na existující soubor nemá nikdy za následek přepsání stávajíícho souboru, ale soubor je vždy otevřen pro zápis na konec souboru. Čtení je možné provádět z kterékoliv pozice, každý zápis je prováděn na konec souboru a to i v případě, že aktuální pozice v souboru dosažená např. funkcí seek není na konci souboru.

Roury

Pomocí funkce open je možné posílat data na vstup externím programům nebo získávat data z externích programů. Docílí se toho použitím znaku | před nebo za jménem souboru. V prvním případě čteme data z výstupu programu, ve druhém případě posíláme data na standardní vstup externího programu.

# data posíláme na vstup programu lpr, který 
# tiskne na tiskárnu
open PRINTER, '| lpr -Plp';

# čteme výstupní data programu ls s argumenty -la
open LS, 'ls -la |';

Komunikace s takto spuštěnými externími příkazy se děje prostřednictvím standardního vstupu, případně standardního výstupu. Chybový výstup zachytáván není.

Čtení ze souboru pomocí operátoru < >

Ve skalárním kontextu tento operátor přečte z ovladače, jehož jméno je uvedeno uvnitř špičatých závorek, další řádek a tento řádek vrátí. V případě, že se nacházíme na konci souboru nebo v případě chyby vrátí nedefinovanou hodnotu. Kde končí řádek je definováno obsahem proměnné $/ ($INPUT_RECORD_SEPARATOR). Implicitně je tato proměnná nastavena na hodnotu \n, ale je možné do ní přiřadit libovolný řetězec. Máme-li odstavce v textu odděleny jedním prázdným řádkem, a chceme-li, aby byl najednou přečten celý odstavec, nastavíme tuto proměnnou na řetězec \n\n.

V seznamovém kontextu jsou vráceny všechny řádky od aktuální pozice v souboru až do konce souboru tak, že každý prvek seznamu odpovídá jednomu načtenému řádku za souboru.

Uvnitř tohoto operátoru se může nacházet jakýkoliv výraz. Je-li to jednoduchá skalární proměnná, obsahuje buď jméno ovladače, nebo typeglob se jménem ovladače nebo odkaz na typeglob se jménem ovladače. Tato proměnná musí být ve tvaru $jmeno. Cokoliv složitějšího je bráno jako vzor pro jméno souboru, který se předá funkci glob. Návratová hodnota této funkce je interpretována jako seznam jmen souborů, která potom vrátí i tento operátor. Je tomu tak i v případě, že se uvnitř špičatých závorek nachází prvek pole nebo hashe (což jsou také skalární hodnoty), ale i proměnná ve tvaru ${jmeno}. To je také způsob, jak vynutit řetězcové zpracování výrazu v < >, tzn. provedení vkládání hodnot proměnných.

print "Soubory s příponou txt:\n";
print "$_\n" for <*.txt>;

Zvláštní chování má tento operátor v případě, že není zadáno jméno ovladače. V tomto případě operátor prochází soubory, jejichž jména jsou obsažena v poli @ARGV. Toto pole obsahuje seznam argumentů zadaných skriptu na příkazové řádce. Je-li toto pole prázdné, pracuje se se souborem -.

Každý soubor z pole @ARGV je implicitně otevřen funkcí open a přidružen k ovladači ARGV (zápis < > ke vlastně synonymem pro <ARGV>). Způsob otevření souboru závisí na tom, v jakém tvaru je jméno souboru zadáno. Je-li zadáno jen samotné nebo se znakem < na začátku, je soubor otevřen pouze pro čtení. Podobně mohou být použity všechny další znaky -- >, >>, |, případně v kombinaci s + pro současný zápis i čtení. Jméno takto otevřeného souboru je uloženo ve speciální proměnné $ARGV, do které je přiřazen prvek z pole @ARGV odebraný z tohoto pole funkcí shift. Po ukončení procházení souborů v poli @ARGV je tedy toto pole prázdné a při dalším použití operátoru < > je pracováno se souborem -.

Soubory jsou zpracovány tak, jako by se jednalo o jeden soubor. Proměnná $. obsahující číslo aktuálně zpracovávané řádky se po otevření nového souboru nevynuluje.

# skript spuštěn s argumenty 'data1', 'data2'
while (< >) {
     print "$.: $_";
}

# vytiskne
1: data1 - řádek 1
2: data1 - řádek 2
3: data2 - řádek 1
4: data2 - řádek 2
5: data2 - řádek 3

Nejsou-li na příkazové řádce žádné argumenty, pracuje se se souborem -, tzn. zpracovává se standardní vstup.

while ($radek = < >) {
    # vytiskne zadané řádky velkými písmeny
    print uc $radek;
}

Není-li hodnota získaná pomocí tohoto operátoru někam uložena, je ztracena stejně jako u ostatních operátorů či funkcí. Jedinou výjimku tvoří použití jako jediného výrazu uvnitř cyklu while. V tom případě dojde k automatickému uložení do proměnné $_.

while (< >) {
    # v proměnné $_ máme načtený řádek,
    # zde provádíme jeho zpracování
}

Soubor -

Soubor se jménem - (minus) reprezentuje standardní vstup v případě otevření pro čtení a standardní výstup v případě otevření pro zápis. Tento soubor je automaticky přidán do pole @ARGV v případě, že používáme operátor < > na jeho procházení a toto pole je prázdné.

Otevřeme-li soubor - pomocí znaku | pro čtení nebo pro zápis, bude nejprve znovu spuštěn aktuální skript a potom mu budou posílána data na vstup nebo bude zachytáván jeho výstup. Jedná se o skrytou variantu příkazu fork.

Chceme-li zpracovávat soubor na disku, který se jmenuje - (jméno zadáno buď jako literál nebo jako argument skriptu), funkce open s argumentem - otevře vždy standardní vstup nebo výstup. V tomto případě musíme použít jméno souboru s plnou nebo relativní cestou, tzn. /home/darena/- nebo ./-.

Otevření souboru pomocí funkce sysopen

Pomocí funkce sysopen je možné otevřít soubor stejně jako funkcí open, ale je navíc k dispozici více možností otevření. Funkce přebírá tři nebo čtyři argumenty. Prvními dvěma jsou jako u funkce open jméno ovladače a jméno souboru. Třetím argumentem je režim, v jakém má být soubor otevřen. V případě funkce sysopen je způsob otevření souboru zadán jako samostatný argument. Pro určení způsobu otevření můžeme použít konstanty z modulu Fcntl začínající na O_ (jsou automaticky importovány v okamžiku použití use Fcntl).

sysopen FILE, $jmeno, O_WRONLY | O_APPEND | O_CREAT;
# otevře soubor pro zápis, data budou připojena na konec, neexistuje-li
# soubor, vytvoří se;
# to samé jako open FILE, ">>", $jmeno;

sysopen FILE, $jmeno, O_WRONLY | O_EXCL | O_CREAT;
# otevře soubor pro zápis, neexistuje-li soubor, vytvoří se,
# soubor před otevřením nesmí existovat;
# nemá ekvivalent s využitím funkce open

Binární soubory

Obsahují-li soubory binární data, je potřeba toto explicitně vyjádřit (při práci s rourami a sockety je toto děláno automaticky). Toho se docílí použitím funkce binmode na příslušný ovladač souboru. Pak je možné pracovat s těmito soubory obvyklým způsobem.

binmode INPUT;
binmode OUTPUT;
while ( <INPUT> ) { print OUTPUT; }

Obsah binárních soborů je různý u různých operačních systémů a binární režim je třeba nastavit jen někde (operační systému typu UNIX to nevyžadují, systémy DOS či Windows ano). Rozdíl je v chápání znaku konce řádku. Tam, kde není binární režim třeba, je konec řádku tvořen jedním symbolem (\n), zatímco např. ve Windows jsou to znaky dva (\n a \r). Práce s textovými a binárními souboru je tedy odlišná a nerespektování této skutečnosti by mohlo vést k poškození dat.

Pro čtení bloku znaků ze souboru použijeme funkci sysread. Ta provede načtení dat zadané délky ze zadaného ovladače a tento obsah uloží do zadané skalární proměnné. Jako návratová hodnota je vrácen počet skutečně přečtených znaků. Proto je možné tuto funkci použít i pro testování konce souboru.

K zápisu bloku znaků do souboru slouží funkce syswrite. Ta zapíše do zadaného ovladače data zadané délky ze zadané proměnné. Vrátí počet znaků, které se skutečně podařilo zapsat.

Aktuální pozici v souboru je možné nastavit funkcí sysseek.

Není dobré kombinovat použití funkcí print, read, ogetc, eof, operátoru < > a funkcí sysread či syswrite. Je to proto, že první skupina funkcí využívá vyrovnávací paměť, zatímco druhá skupina funkcí ji ignoruje.

© 2003, František Dařena

Valid XHTML 1.0! Valid CSS!