LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> Java (17) - práce se soubory

Velmi častým prostředkem komunikace programu s okolním světem je soubor. Protože je Java koncipována jako platformově nezávislá, musí se vypořádat s různými nástrahami, které rozličné souborové systémy skýtají. Že to není jednoduchý úkol, ale že není neřešitelný, se přesvědčíme při zkoumání javovských prostředků, které jsou pro tento účel k dispozici.

1.9.2005 06:00 | Lukáš Jelínek | Články autora | přečteno 64838×

Úkol nelehký

Pokud potřebujeme uložit data z programu (nebo je načíst), ve většině případů k tomu použijeme soubor. A přestože soubor samotný se skoro na všech platformách tváří úplně stejně (je to různě dlouhá posloupnost bajtů), způsob práce se liší, a to dost zásadně.

Zajímají nás především dvě nejrozšířenější skupiny souborových systémů:

  • systémy unixového typu - je jich celá řada (UFS, Ext2, ReiserFS atd.), pro všechny z nich je typické, že celý systém tvoří jediný adresářový strom, jako oddělovač jednotlivých úrovní se používá běžné (dopředné) lomítko, rozlišují se velká a malá písmena, téměř všechny znaky ASCII jsou platné a pro ukládání názvů lze obecně používat různé kódové stránky.
  • systémy firmy Microsoft (FAT a NTFS) - rozlišují se jednotlivá logická zařízení ("písmena disků"), oddělovačem je zpětné lomítko, velikost písmen se nerozlišuje, okruh platných znaků je poměrně malý (i když to některé systémové funkce řádně nekontrolují a lze tak na disk propašovat i soubory s neplatnými názvy, které už pak nelze odstranit), kódování se liší podle jednotlivých systémů (NTFS používá Unicode, FAT potom staré kódové stránky, např. pro češtinu CP 852). Situaci ještě komplikuje používání názvů souborů UNC.

Speciální situace se pak řeší, pokud se nějaký souborový systém používá z operačního systému, ve kterém je cizí (typicky třeba FAT v Linuxu). Pak platí pravidla, která jsou směsicí výše uvedeného a je třeba si dávat zvlášť velký pozor.

Třída File

V balíku java.io najdeme třídu, která nám poskytne vše potřebné k práci se soubory - je to třída File. Nepředstavuje přímo konkrétní soubor, nýbrž tzv. abstraktní cestu (tedy obecně jakoukoli cestu identifikující nějaký soubor). Může odkazovat na platný soubor, ale také nemusí. Důležité ale je, že je to (podobně jako třeba String) invariant, jak se jednou vytvoří, už nelze změnit.

V řadě tříd ze standardní knihovny Javy najdeme metody, které vyžadují jako svůj argument název souboru. Prakticky ve všech případech lze použít jak textový řetězec (String; to jsme doposud běžně dělali), tak právě instanci třídy File, což je přenositelnější a robustnější řešení, protože můžeme již předem zjistit o daném názvu souboru nějaké informace nebo provést se souborem potřebné operace. Jako příklad mohu uvést třeba konstruktor třídy FileInputStream.

Specifikace abstraktní cesty

Zdůrazňuji, že objekt File může představovat jak soubory (běžné, ale i speciální, třeba soubory zařízení), tak adresáře. Cesta může být absolutní i relativní, může být dokonce i prázdná. Abstraktní cesta se vždy skládá z prefixu (např. označení kořenového adresáře, úvodní označení UNC cesty; u relativních cest prefix samozřejmě chybí) a z posloupnosti názvů jednotlivých adresářových úrovní (samozřejmě včetně případného názvu souboru na konci) oddělených separátorem.

Jak se s cestami pracuje, záleží na nastavení vlastností systému (system properties; někdy později se na ně podíváme důkladněji), a toto nastavení se samozřejmě liší podle platformy. Nejdůležitější je oddělovač názvů v cestě; ve třídě File je určen hodnotou konstanty separatorChar (znakové vyjádření), resp. separator (řetězcové vyjádření). Na unixových systémech je oddělovačem samozřejmě dopředné lomítko, na microsoftích systémech lomítko obrácené.

Když už jsme u těch konstant, třída File obsahuje ještě konstanty pathSeparatorChar a pathSeparator. Tyto konstanty představují oddělovač cest (v případech kdy máme zapsaných několik cest za sebou) a mají hodnotu dvojtečky (unixové systémy), resp. středníku (microsoftí systémy).

Vytvoření instance File

Jak jsem již řekl, jednou vytvořený objekt File už nemůžeme měnit. Má to svoji logiku, protože jestliže řetězec je v Javě neměnný, musí být jeho speciální případ (což abstraktní cesta bezpochyby je) také neměnný.

Objekt File můžeme vytvořit třemi způsoby: názvem souboru, názvem souboru vzhledem k rodiči, a pomocí URI (Uniform Resource Identifier; viz RFC 2396). Pokud ho vytáříme přímo z celé (absolutní nebo relativní) cesty, je situace jednoduchá, řetězec se pouze převede na abstraktní cestu. Pokud je dán rodič (ať už názvem nebo instancí File, a není null), je abstraktní cesta vytvořena jako relativní vůči této rodičovské cestě (adresáři), resp. proti výchozímu adresáři (pokud je rodič prázdná abstraktní cesta). Pokud se instance File vytváří z URI, musí být splněny určité požadavky (schéma musí být "file", cesta nesmí být prázdná atd.).

Separátor v řetězci nemusí odpovídat dané platformě. Pokud je konstruktor schopen ho normalizovat (tzn. převést do správné podoby), zpracuje se cesta bez problémů.

Operace s instancí třídy File

Práce s cestou k souboru

Protože, jak bylo řečeno, je objekt File abstraktní cestou k souboru, můžeme s touto cestou pracovat a získávat různé její varianty. Lze získat celou cestu v různých podobách, části cesty a některé další verze.

  • getPath() - vrátí (normalizovanou) abstraktní cestu v podobě, jak byla zadána při vytváření objektu (tedy absolutní zůstane absolutní atd.). Stejný efekt má i metoda toString().
  • getAbsolutePath() - vrátí cestu převedenou do absolutního tvaru. Mechanismus případného převodu z relativní cesty na absolutní je platformově závislý, většinou se ale jako báze použije domovský adresář uživatele.
  • getCanonicalPath() - vrací kanonický tvar cesty. V praxi to znamená, že se pokusí cestu maximálně "vyhodnotit", zpracovat. Odstraní všechny označení stejného nebo nadřazeného adresáře (tečku a dvě tečky), zpracuje symbolické odkazy atd.. Chování je silně platformově závislé a liší se podle toho, zda cesta (nebo její části) existuje či nikoli.
  • getName() - získá z cesty pouhý název souboru (bez adresářové cesty).
  • getParent() - vrací rodičovský adresář souboru. Vychází se pouze z cesty, soubor ani rodičovský adresář nemusí existovat.
  • isAbsolute() - zjistí, zda je cesta absolutní.
  • compareTo() - lexikograficky porovná tuto abstraktní cestu s jinou (ani jedna nemusí existovat). Porovnávání je platformově závislé, podle systému se použije rozlišení malých/velkých písmen. Podobně pracuje metoda equals(), která pouze zjišťuje, zda jsou abstraktní cesty totožné.

Všechny uvedené metody, které vrací cestu nebo její část, mají jako návratovou hodnotu textový řetězec. Kromě nich existují jejich obdoby, které vracejí novou instanci typu File. Jsou to metody getAbsoluteFile(), getCanonicalFile() a getParentFile().

Abstraktní cestu lze také převést (metodou getURL()) na odpovídající URL, anebo na URI (metodou getURI()). Protože však metoda getURL() neumí správně naložit se zakázanými znaky, doporučuje se vždy použít getURI() a získaný objekt převést jeho metodou getURL() na URL (protože URL jsou podmnožinou URI, často se ve skutečnosti ani vnitřně nic převádět nebude).

Zjišťování informací o souboru

Nyní už přejdeme k operacím, které se týkají souboru, na který abstraktní cesta odkazuje. Množina operací není velká, musí být totiž dostatečně přenositelná mezi platformami.

  • exists() - základní věc: zjištění, zda vůbec soubor existuje. Pokud se chystáme dělat s ním nějaké další věci, je dobré zavolat nejprve tuto metodu a ověřit si jeho přítomnost.
  • isFile(), isDirectory() - pomocí těchto metod poznáme, zda se jedná o "normální" soubor nebo o adresář. Opět je to platformově závislé, např. symbolický odkaz na soubor se tváří jako běžný soubor. Platí ale, že jakékoli soubory/adresáře vytvořené z Javy zcela jistě projdou správně těmito testy.
  • canRead(), canWrite() - dozvíme se, zda můžeme číst či zapisovat do daného souboru. Pokud byl ale přístup odepřen, už se nedozvíme proč. To je daň za přenositelnost, nemáme možnost zjišťovat třeba přístupová práva.
  • isHidden() - zjišťuje, zda je soubor označen jako "skrytý". V unixových systémech za skryté soubory považuje ty, jejich název začíná tečkou, ve Windows pak soubory s nastaveným atributem "hidden".
  • length() - zjistí velikost souboru. Protože vrací hodnotu typu long, není problém ani s opravdu velkými soubory.
  • lastModified() - jediný časový údaj, který můžeme o souboru zjistit, je čas poslední modifikace. Ne všechny souborové systémy poskytují další časové informace, proto je to takto omezeno. Navíc v praxi je to právě ten nejpotřebnější údaj, podle něhož můžeme např. zjišťovat, že někdo změnil konfigurační soubor.

Adresářové informace

Předchozí metody se týkaly všech souborů bez rozdílu, tedy včetně adresářů. Pro adresáře samotné máme k dispozici speciální sadu metod, které využijeme pro přístup k souborům v těchto adresářích:

  • list() - nejjednodušší varianta. Prostě vrátí pole obsahující seznam všech souborů v daném adresáři - položky tohoto pole budou textové řetězce s názvy souborů. Pořadí souborů není definováno, může být libovolné (závisí na implementaci; v Linuxu budou soubory pravděpodobně uspořádány tak, jak jsou zaznamenány v adresáři). To samozřejmě není problém, protože si soubory můžeme (ať už podle názvu nebo jinak) seřadit podle potřeby.
  • listFiles() - dělá přesně totéž co list(), ale místo pole textových řetězců vrací pole objektů File, tedy abstraktních cest. Zda použijeme tuto nebo přechozí metodu, záleží na konkrétní situaci.
  • list(FilenameFilter f) - modifikace metody list() s tím, že předem vybíráme jen některé soubory. Které to budou, to určí implementace rozhraní FilenameFilter. O filtraci ještě bude řeč.
  • listFiles(FilenameFilter f) - opět metoda vracející pole objektů File, tentokrát s filtrací (viz výše).
  • listFiles(FileFilter f) - další modifikace, ale s jiným typem filtru.
  • listRoots() - v souborových systémech unixovského typu je hierarchie přísně stromová, vždy máme jediný kořen. V jiných systémech to ale platit nemusí (a také neplatí), proto je třeba mít možnost dostat se ke všem dostupným kořenům - a to zajišťuje právě tato statická metoda. Vrací pole všech kořenů adresářových stromů, které jsou v danou chvíli k dispozici.

O filtraci souborů

Výše uvedené metody provádějí filtraci souborů podle poskytnutého rozhraní. To je typická ukázka toho, jak se v Javě podobné věci řeší - existuje mnoho a mnoho objektových metod, které jako mají parametr nějaké jednoduché rozhraní, obsahující třeba jen jedinou metodu. Chování je pak plně v režii implementace tohoto rozhraní.

Pokud budeme implementaci potřebovat jen v jednom jediném případě, s výhodou využijeme možnosti vytvořit anonymní třídu přímo na daném místě. Viz příklad:

File f = new File("/home/username/docs");       // vybereme adresář

String list[] = f.list(new FilenameFilter() {
    boolean accept(File dir, String name) {
        return name.endsWith(".pdf");           // jen názvy *.pdf
    }
});

Arrays.sort(list);    // abecední seřazení

for (int i=0; i<list.length; i++) {
    System.out.println(list[i]);
}

Uvedený příklad vypíše v abecedním pořadí všechny soubory z daného adresáře, jejichž název končí na .pdf (jsou to tedy dokumenty formátu PDF).

Manipulační operace

Zatím jsme o souborech pouze zjišťovali různé informace. S tím si rozhodně nelze vystačit, občas musíme také někde něco změnit. Třída File nabízí několik manipulačních operací, tak se na ně podívejme:

  • renameTo(File f) - metoda přejmenuje soubor podle zadání. Všimněte si, že se jako parametr zadává jiná instance objektu File. Jak jsme si již řekli, instance File je neměnná, proto i po úspěšném přejmenování souboru zůstane tak, jak je (bude obsahovat původní cestu k souboru). Naopak nové jméno souboru bude odpovídat zadané instanci, o čemž se můžeme přesvědčit tak, že zavoláme metodu exists(). Chování je silně platformově závislé, nemůžeme spoléhat, že metoda bude dělat vždy to, co dělala na některé platformě. Návratovou hodnotu je třeba vždy testovat.
  • delete() - pokusí se smazat soubor. Pokud to jde, smaže ho. Neprázdné adresáře mazat nelze, musí se nejdřív explicitně vyprázdnit.
  • deleteOnExit() - zajímavá metoda, naplánuje smazání souboru při ukončování programu. Zafunguje pouze při čistém ukončení programu, tedy ne při "sestřelení" (na Linuxu signálem SIGKILL) nebo při zavolání metody System.halt(). Metoda se používá pro automatické mazání dočasných souborů (viz níže). Pozor - naplánované smazání už nejde zrušit!
  • createNewFile() - vytvoří nový prázdný soubor. Metoda nemá příliš velké využití, ale někdy se hodí.
  • mkdir(), mkdirs() - dvojice metod pro vytváření adresářů. Liší se pouze tím, že ta první vytvoří pouze ten jediný adresář, na který odkazuje instance File, kdežto ta druhá vytvoří, pokud je třeba, i všechny nadřazené adresáře.
  • setLastModified(long time) - změní časový údaj o poslední změně souboru. Někdy se to může hodit.
  • setReadOnly() - nastaví, že soubor bude pouze ke čtení. Nepříjemné je, že to je pouze jednosměrná operace a v Javě nemáme prostředky, jak to vrátit zpět.

Následující příklad ukáže několik operací provedených na souboru identifikovaném abstraktní cestou. Nejdříve se adresářová cesta převede do kanonického tvaru, potom se daný adresář vytvoří, v něm se založí nový soubor a ten se nakonec přejmenuje. Všimněte si, že některé operace vyžadují ošetření výjimky IOException.

File dir = new File("../user2/texty");
try {
    dir = dir.getCanonicalFile();
} catch (IOException e) {
    System.err.println("Nelze ziskat kanonicky tvar: " + dir);
    System.exit(1);
}

if (!dir.makedir()) {
    System.err.println("Nelze vytvorit adresar " + dir);
    System.exit(2);
}

File f = new File(dir, "test.txt");
try {
    if (!f.createNewFile()) {
        System.err.println("Soubor " + f + " jiz existuje");
    }
} catch (IOException e) {
    System.err.println("Nelze vytvorit soubor " + f);
    System.exit(3);
}

File f2 = new File(f.getParent(), "test2.txt");
if (!f.renameTo(f2)) {
    System.err.println("Nelze prejmenovat soubor na " + f2);
    System.exit(4);
}

Dočasné soubory

Občas potřebujeme uložit nějaká data do dočasného souboru, abychom je použili později. Lze to samozřejmě udělat ručně, tedy zvolením nějakého umístění souboru, to ale nebude přenositelné. Ve třídě File máme k dispozici dvě statické metody, kterými tento problém snadno vyřešíme:

  • createTempFile(String prefix, String suffix) - vytvoří dočasný soubor s daným prefixem (min. 3 znaky dlouhým) a danou koncovkou (může být null, pak se použije .tmp). Soubor vytvoří v adresáři pro dočasné soubory (např. /tmp), vrací instanci třídy File.
  • createTempFile(String prefix, String suffix, File directory) - od předchozí metody se liší tím, že umožňuje specifikovat adresář pro uložení souboru (pokud je null, chování této metody je shodné s chováním té předchozí).

Vytvořený soubor se nemaže automaticky při skončení programu. Pokud to potřebujeme (což je skoro vždy), použijeme metodu deleteOnExit().

Od souborů k síti

Kapitola (pravda, poněkud "výčtově" orientovaná) o práci se soubory tímto dospěla ke svému konci. Ke konci ale nedospěly I/O operace, protože Java v této oblasti nabízí velmi mnoho. Příště přijde řada na komunikaci po síti, která je v dnešní době stejně důležitá jako práce se soubory.

Verze pro tisk

pridej.cz

 

DISKUZE

metoda accept 16.8.2006 10:42 Martin Landa
makedir ve 2. příkladu 17.8.2006 07:59 Martin Landa
Spouštění jiné aplikace 23.12.2008 14:11 Jiří "Akres" Kantor
L Re: Spouštění jiné aplikace 23.12.2008 15:31 Aleš Hakl
  L Re: Spouštění jiné aplikace 23.12.2008 16:48 Jiří "Akres" Kantor
    L Re: Spouštění jiné aplikace 23.12.2008 18:12 Jiří "Akres" Kantor
      L Re: Spouštění jiné aplikace 24.12.2008 01:06 Aleš Hakl
Komunikace se souborem 12.3.2010 05:35 Lukáš Vlasatý
DBF súbory 15.11.2013 14:26 Peter




Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

13.9.2017 8:00 /František Kučera

Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt – tentokrát netradičně v pondělí: 18. září od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).


Přidat komentář

3.9.2017 20:45 /Redakce Linuxsoft.cz
PR: Dne 21. září 2017 proběhne v Praze konference "Mobilní řešení pro business". Hlavní tématy konference budou: nejnovější trendy v oblasti mobilních řešení pro firmy, efektivní využití mobilních zařízení, bezpečnostní rizika a řešení pro jejich omezení, správa mobilních zařízení ve firmách a další.
Přidat komentář

15.5.2017 23:50 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt, který se bude konat ve čtvrtek 18. května od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

12.5.2017 16:42 /Honza Javorek
PyCon CZ, česká konference o programovacím jazyce Python, se po dvou úspěšných ročnících v Brně bude letos konat v Praze, a to 8. až 10. června. Na konferenci letos zavítá např. i Armin Ronacher, známý především jako autor frameworku Flask, šablon Jinja2/Twig, a dalších projektů. Těšit se můžete na přednášky o datové analytice, tvorbě webu, testování, tvorbě API, učení a mentorování programování, přednášky o rozvoji komunity, o použití Pythonu ve vědě nebo k ovládání nejrůznějších zařízení (MicroPython). Na vlastní prsty si můžete na workshopech vyzkoušet postavit Pythonem ovládaného robota, naučit se učit šestileté děti programovat, efektivně testovat nebo si v Pythonu pohrát s kartografickým materiálem. Kupujte lístky, dokud jsou.
Přidat komentář

2.5.2017 9:20 /Eva Rázgová
Putovní konference československé Drupal komunity "DrupalCamp Československo" se tentokrát koná 27. 5.2017 na VUT FIT v Brně. Můžete načerpat a vyměnit si zkušenosti z oblasti Drupalu 7 a 8, UX, SEO, managementu týmového vývoje, využití Dockeru pro Drupal a dalších. Vítáni jsou nováčci i experti. Akci pořádají Slovenská Drupal Asociácia a česká Asociace pro Drupal. Registrace na webu .
Přidat komentář

1.5.2017 20:31 /Pavel `Goldenfish' Kysilka
PR: 25.5.2017 proběhne v Praze konference na téma Firemní informační systémy. Hlavními tématy jsou: Informační systémy s vlastní inteligencí, efektivní práce s dokumenty, mobilní přístup k datům nebo využívání cloudu.
Přidat komentář

15.4.2017 15:20 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě IoT a radiokomunikace? Přijď na sraz spolku OpenAlt, který se bude konat ve středu 19. dubna od 18:30 v Šenkovně (Sokolská 60, Praha 2).
Přidat komentář

5.3.2017 19:12 /Redakce Linuxsoft.cz
PR: 23. března proběhne v Praze konferenci na téma Cloud computing v praxi. Hlavními tématy jsou: Nejžhavější trendy v oblasti cloudu a cloudových řešení, Moderní cloudové služby, Infrastruktura současných cloudů, Efektivní využití cloudu, Nástrahy cloudových řešení a jak se jim vyhnout.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

18.9.2017 14:37 / Rojas
high security vault

15.9.2017 7:33 / Wilson
new zealand childcare jobs

31.8.2017 12:11 / Jaromir Obr
Re: ukůládání dat ze souboru

30.7.2017 11:12 / Jaromir Obr
Národní znaky

27.7.2017 12:24 / Jaromir Obr
Cteni/zapis

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2017) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze