Ruby po kapkách (17.) – práce se soubory

17. července 2009

V minulém díle jsme se zabývali základy vstupních a výstupních operací. Tentokrát se zaměříme na další podrobnosti ohledně práce se soubory. Nejdříve se však krátce podíváme na možná řešení úkolů z konce minulého dílu.

Prvním zadáním bylo vytvořit program, který spočítá kontrolní součet souboru. Pro zjednodušení nebudeme počítat žádnou skutečně používanou funkcí pro výpočet kontrolního součtu, ale spokojíme se s prostým součtem číselných hodnot jednotlivých bytů. Jedno z možných řešení může vypadat následovně.

suma = 0
File.open(ARGV[0]) do |f|
f.each_byte { |b| suma += b }
end
puts suma

Jak je vidět, otevíráme soubor, jehož název je interpretu předán jako první parametr. Jednotlivé byty jsou pak předávány do bloku, který je přičítá k proměnné, kde uchováváme aktuální součet. Použití vysokoúrovňových metod spolu s bloky nám ušetřilo práci s úkony typu uzavírání souboru nebo testování, zda jsem již dosáhli konce vstupu. Díky tomu, že Ruby bude automaticky upravovat požitelný rozsah celého čísla v proměnné suma (nakonec bude použita třída Fixnum), bude tento triviální přístup fungovat i pro velké soubory. Použití programu na sebe sama vypadá takto:

~$ ruby chksum.rb chksum.rb
6643

Druhý příklad, který si klade za cíl porovnat navzájem dva soubory bude poněkud složitější. Ukážeme si co možná nejjednodušší řešení, ve kterém budeme ignorovat možnost vzniku chyb, protože bychom zatím stejně nevěděli, jak se s nimi vypořádat. Spokojíme se tedy s tím, že každá operace vstupu/výstupu, která skončí chybou zastaví běh programu, což je standardní chování.

f1 = File.new(ARGV[0])
f2 = File.new(ARGV[1])
result = ‚==‘
loop do
b1 = f1.getc
b2 = f2.getc
result = ‚!=‘ unless b1 == b2
break unless b1 and b2 and b1 == b2
end
f1.close
f2.close
puts „#{ARGV[0]} #{result} #{ARGV[1]}“

Otevíráme tentokrát zároveň dva soubory, jejichž jména zadáme jako první dva parametry příkazové řádky. Ve smyčce pak z obou souborů načteme po jednom bytu a pokud se byty nerovnají přenastavíme implicitní stav, že soubory jsou totožné. Následuje podmínka, která může ukončit smyčku v případě, kdy je jedna z hodnot b1 a b2 vyhodnocena jako false nebo když se hodnoty nerovnají. Vzpoměňme si, že metoda getc vrací nil, když narazí na konec souboru, a že nil se vyhodnocuje jako false. Dále korektně uzavřeme oba soubory, i když v tomto případě, kdy program vzápětí končí bychom mohli spoléhat na to, že soubory uzavře operační systém po ukončení činnosti interpretu. Spuštění a porovnání sama se sebou a s jiným souborem vypadá následovně.

~$ ruby compare.rb compare.rb compare.rb
compare.rb == compare.rb
~$ ruby compare.rb compare.rb chksum.rb
compare.rb != chksum.rb

A nyní se již pojďme podívat na nové věci. Zatím jsme například všechny soubory otevírali jen pro čtení. Pokud bychom do nich chtěli zapisovat, musíme použít další parametr metody new. Parametr je řetězec, který může obsahovat několik znaků s následujícím významem (který je převzat ze standardní knihovny jazyka C).

  • r – otevření souboru pro čtení (implicitní volba)
  • r+ – otevření pro čtení i zápis, začíná se na začátku souboru
  • w – otevření jen pro zápis, kdy je soubor vytvořen nebo přepsán, pokud již existuje
  • w+ – jako w, ale navíc i čtení
  • a – jako w, ale soubor se nepřepíše, pokud existuje, začne se na konci souboru
  • a+ – jako a, ale navíc i čtení
  • b+ – binární mód na platformě MS Windows

Krátká relace v irb demonstruje otevření souboru pro zápis a očíslování prvních deseti řádků. Při opakovaném spouštění by byl soubor vždy přepsán. Avšak pokud bychom změnili mód otevírání souboru na „a“, docházelo by k rozšiřování souboru o další řádky.

irb(main):001:0> f = File.new(‚test‘, ‚w‘)
=> #<File:test>
irb(main):002:0> 1.upto(10) { |i| f.puts i }
=> 1
irb(main):003:0> f.close
=> nil

Ve třídě File je k dispozici řada metod pro zjišťování nebo nastavování různých vlastností souboru a pro manipulaci se soubory. Některé z těchto metod jsou metodami třídy, některé metodami instance a některé jsou dostupné v obou variantách. Pro podrobnější popis je třeba sáhnout po referenční příručce. My si ukážeme vybrané metody opět na příkladu relace v irb.

irb(main):002:0> f = File.new(‚test‘, ‚w‘)
=> #<File:test>
irb(main):003:0> f.puts ‚bflmpsvz‘
=> nil
irb(main):004:0> f.atime
=> Thu Jul 16 21:25:53 +0200 2009
irb(main):005:0> f.mtime
=> Thu Jul 16 21:36:13 +0200 2009
irb(main):006:0> f.ctime
=> Thu Jul 16 21:36:13 +0200 2009
irb(main):007:0> f.close
=> nil
irb(main):008:0> File.atime(‚test‘)
=> Thu Jul 16 21:25:53 +0200 2009
irb(main):009:0> File.size(‚test‘)
=> 9
irb(main):010:0> File.truncate(‚test‘, 5)
=> 0
irb(main):011:0> File.size(‚test‘)
=> 5
irb(main):012:0> File.rename(‚test‘, ‚best‘)
=> 0
irb(main):013:0> File.delete(‚best‘)
=> 1

Na začátku opět vytvoříme soubor s názvem „test“. Série metod instance zjišťuje různé časy. Popořadě to je poslední čas přístupu k souboru, poslední čas modifikace souboru (obsahu) a čas poslední změny informací o souboru (v adresáři). Tyto metody jsou zároveň i metodami třídy, což je demonstrováno voláním metody File.atime. U metod třídy je pochopitelně nutné zadávat jako první parametr cestu k souboru, na který se má operace aplikovat. Následuje metoda pro zjištění velikosti souboru v bytech a metoda pro zmenšení velikosti maximálně na daný počet bytů (zbytek je ztracen). Poslední dvě metody, jak již jejich názvy napovídají, slouží k přejmenování a smazání souborů.

Na různých platformách, kde je možné používat Ruby, se jako oddělovače adresářů v cestě k souboru používají buď normální nebo zpětná lomítka. Ruby proto má mechanismus pro přenositelnou konstrukci a dekonstrukci kompletní cesty. Oddělovač na dané platformě je uložen v konstantě File::SEPARATOR. Následující irb relace pochází z unixového prostředí.

irb(main):001:0> File::SEPARATOR
=> „/“
irb(main):002:0> path = ‚/dir1/dir2/dir3/file‘
=> „/dir1/dir2/dir3/file“
irb(main):003:0> File.split(path)
=> [„/dir1/dir2/dir3“, „file“] irb(main):004:0> File.dirname(path)
=> „/dir1/dir2/dir3“
irb(main):005:0> File.basename(path)
=> „file“
irb(main):006:0> File.join(‚dir‘, ‚subdir‘, ‚file‘)
=> „dir/subdir/file“

V příštím díle se budeme zabývat například možnostmi procházením adresářů. Již teď ale máme veškeré znalosti, abychom byli schopni vytvořit program pro kopírování souboru (cesta k extujícímu souboru a cesta ke kopii bude zadána jako parametr příkazové řádky). Konkrétní podobu programu ponecháme jako domácí úkol. Jako jistou inspiraci lze použít příklad s porovnáváním souborů.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *