V minulém díle jsem rozšířili naše znalosti vstupně výstupních operací o další podrobnosti práce se soubory. Tentokrát se podíváme na to, jak v Ruby procházet adresáře. Nejdříve se ale podívejme na příklad s kopírováním souboru, kterým jsme posledně končili. Možná nejjednodušším řešením, které se poněkud podobá již probíranému porovnávání souborů, je následující kód.

f1 = File.new(ARGV[0])
f2 = File.new(ARGV[1], ‚w‘)
while buffer = f1.read(2048) do
f2.write(buffer)
end
f1.close
f2.close

Takto můžeme pomocí prográmku vytvořit jeho vlastní kopii poté, co zdrojový kód uložíme do souboru s názvem copy.rb.

~$ ruby copy.rb copy.rb copycopy.rb

Soubor, jehož název získáme jak první parametr z příkazové řádky, otevřeme pro čtení. Soubor s názvem z druhého parametru otevřeme pro změnu pro zápis. Pak ve smyčce načítáme do pomocné proměnné vždy maximálně dva kilobyty ze zdrojového souboru a výsledek zapisujeme do cílového souboru. Ve smyčce využíváme skutečnosti, že metoda read vrací hodnotu nil v případě, že dosáhne konce souboru. To smyčku ukončí – nil se vyhodnotí jako false. (Důležité je uvědomit si, že se testuje hodnota celého výrazu přiřazení, která se ale rovná pravé části výrazu. Otázka na zamyšlení: co by se stalo, kdybychom udělali překlep a místo operátoru přiřazení = použili porovnávací operátor == ?) Na konec oba soubory korektně zavřeme. Opět je třeba připomenout, že zatím nepředpokládáme a nijak neřešíme možnost vzniku chyby. K této problematice se ještě dostaneme v příštích dílech. Teď však vzhůru na adresáře.

Pro reprezentaci adresářů má Ruby vestavěnou třídu Dir. Nová instance se vytvoří voláním metod new s řetězcovým parametrem určujícím cestu k příslušnému adresáři. Ještě než se podíváme na specifické metody třídy Dir, prozradíme si rovnou příjemné překvapení – Dir je enumerable. Můžeme tedy používat již známé a pohodlné metody pro procházení kolekcí. Položkami kolekce jsou v tomto případě podadresáře a soubory obsažené v adresáři.

irb(main):001:0> d = Dir.new(‚.‘)
=> #<Dir:0x28313030>
irb(main):002:0> d.to_a.join(‚, ‚)
=> „., .., .links, .xinitrc, .mc, .Xauthority, .vnc, .fontconfig, .inputrc, .gkrellm2, .profile, tmp, .bash_history, .ssh, .bashrc, .gem, copy.rb“
irb(main):003:0> d.grep(/copy/).join(‚, ‚)
=> „copy.rb“

Aniž bychom vytvářeli instanci adresáře, můžeme jím procházet pomocí indexovacího operátoru aplikovaného přímo na třídu Dir (Vzpoměňte si, že indexovací operátor je vlastně metoda jako každá jiná, takže v tomto případě metoda třídy Dir). Indexem může být výraz, který má podobná pravidla jako expanzní znaky v shellu: * nahrazuje libovolný řetězec, ? nahrazuje jeden libovolný znak. Navíc je možné použít třídu znaků v hranatých závorkách. Specialitou je použití kombinace **, která odpovídá rekurzivně podadresářům. Lze tak projít celou adresářovou strukturu. Výsledkem je pole nalezených názvů.

irb(main):004:0> Dir[‚*.rb‘]
=> [„copy.rb“]
irb(main):005:0> Dir[‚**/*.rb‘].size
=> 86

Mezi metodami třídy Dir jsou dále zajímavé ty, které umožňují běžnou manipulaci s adresáři. Názvy metod jsou podobné jako odpovídající unixové příkazy. Následující relace irb ukazuje vytvoření podadresáře, přepnutí aktuálního adresáře na nově vytvořený podadresář, návrat zpět a vymazání podadresáře.

irb(main):001:0> Dir.mkdir(‚test‘)
=> 0
irb(main):002:0> Dir.chdir(‚test‘)
=> 0
irb(main):003:0> Dir.pwd
=> „/home/dali/test“
irb(main):004:0> Dir.chdir(‚..‘)
=> 0
irb(main):005:0> Dir.rmdir(‚test‘)
=> 0

Když už jsem u těch metod s názvy podobnými unixovým příkazům, nedá mi zmínit velmi silnou metodu test, která dělá totéž, co stejnojmenný unixový příkaz – testuje platnost různých předpokladů o souborech nebo o nich zjišťuje informace. Metoda patří přímo třídě Object a můžeme ji tedy volat bez příjemce (úplně přesně řečeno je metoda definována v modulu Kernel, který je do třídy Object namixován). Následující relace zjistí popořadě čas přístupu, změny a modifikace (tak, jak byly definovány v minulém díle) a vrátí je jako instanci třídy Time. Pak zjišťuje, zda se v několika případech jedná o adresář nebo standardní soubor.

irb(main):001:0> test ?A, ‚copy.rb‘
=> Thu Jul 23 19:50:01 +0200 2009
irb(main):002:0> test ?C, ‚copy.rb‘
=> Thu Jul 23 22:40:34 +0200 2009
irb(main):003:0> test ?M, ‚copy.rb‘
=> Thu Jul 23 19:49:43 +0200 2009
irb(main):004:0> test ?d, ‚copy.rb‘
=> false
irb(main):005:0> test ?d, ‚tmp‘
=> true
irb(main):006:0> test ?f, ‚copy.rb‘
=> true
irb(main):007:0> test ?f, ‚tmp‘
=> false
irb(main):008:0> test ?f, ‚xxx‘
=> false

Povšimněte si, že prvním parametrem, který určuje druh testu, je ve skutečnosti integer. Otazník se znakem je v tomto případě speciálním literálem, který vrací kód znaku. Mnemotechnicky pak parametry odpovídají ASCII kódům znaků, které nějak souvisí s testem (?d – jedná se o adresář – directory, ?f – jedná se o soubor – file). Podrobnější výpis všech dostupných testů poskytne jako obvykle referenční příručka na http://www.ruby-doc.org/. Poněkud více objektově orientovaně se ke všem funkcionalitám, které nabízí metoda test, dostaneme pomocí volání metody třídy File.stat. Tato metoda vrací instanci třídy File::Stat, která zapouzdřuje metainformace o souboru a nabízí různé metody, kterými lze zjišťovat konkrétní vlastnosti. Názvy metod jsou samovysvětlující, jak je vidět z následující ukázky.

irb(main):010:0> t = File.stat(‚copy.rb‘)
=> #<File::Stat dev=0x68, ino=8435247, mode=0100644, nlink=1, uid=1001, gid=1001, rdev=0x2029a07, size=131, blksize=4096, blocks=4, atime=Thu Jul 23 19:50:01 +0200 2009, mtime=Thu Jul 23 19:49:43 +0200 2009, ctime=Thu Jul 23 22:40:34 +0200 2009>
irb(main):011:0> t.file?
=> true
irb(main):012:0> t.ftype
=> „file“
irb(main):013:0> t.size
=> 131
irb(main):014:0> t.readable?
=> true

Nyní máme dostatek informací (snad kromě toho, že jsme minule explicitně neuvedli, že metoda File.rename kromě přejmenování umí i přesouvat soubory z jednoho adresáře do druhého), abychom mohli vyřešit zajímavou a malinko složitější úlohu z praxe. Vystupuje v ní IP kamera, která přes FTP nahrává do daného adresáře snímky. Snímky se ukládají do souborů s názvem, který odpovídá vzorci n.jpg, kde n je unikátní číslo. V adresáři tak vzniká obrovské množství souborů, se kterými se obtížně pracuje. Úkolem je vytvořit program, který po spuštění přesune všechny obrázky, které jsou starší než z aktuálního dne, do podadresářů, přičemž pro každý den v minulosti, pro který existují obrázky, vytvoří podadresář (pokud již neexistuje). Podadresář se bude jmenovat podle vzorce RRMMDD – písmena podchopitelně kódují příslušný rok, měsíc a den. Program bude spouštěn s parametrem určujícím cestu do adresáře s obrázky. Vzorové řešení uvedeme opět v příštím díle.

Žádný příspěvek v diskuzi

Odpovědět