====== Wejscie i wyjscie ======
===== Klawiatura =====
* ''gets'' pobiera pojedyncza linie danych ze standardowego wejscia,
* ''readlines'' czyta wszystkie linie, dopoki nie napotka terminatora (EOF - End Of File, na przyklad Ctrl+D). Napotkawszy EOF, zwraca linie w tablicy.
===== Pliki =====
File.open("text.txt").each { |line| puts line }
File.new("text.txt", "r").each { |line| puts line }
Obydwa polecenia otwieraja plik do odczytu (''File.open'' - domyslnie, ''File.new'' poprzez "r"). Ponadto ''File.open'' moze przyjac blok kodu, a gdy dzialanie tego sie zakonczy, zamknie plik, co nie dzieje sie w przypadku drugiej metody (trzeba skorzystac z metody ''close'').
File.open("text.txt") do |f|
puts f.gets
end
f = File.new("text.txt", "r")
puts f.gets
f.close
Korzystajac z pierwszej opcji, mamy szybki i prosty sposob na przeprowadzenie operacji w pojedynczej lokalizacji. W drugim przypadku, udostepniamy referencje szerszej "publice".
* ''each_byte'' wczytuje strumien bajt po bajcie. Otrzymujemy wtedy pojedyncze wartosci bajtow a nie znaki. Aby przekonwertowac je na znaki, mozna skorzystac z metody ''chr''.
* ''each_char'' wczytuje strumien znak po znaku. Jest to przydatne, gdy korzystamy z zestawu znakow, w ktorych niektore znaki przedstawiane sa za pomoca wiecej niz jednego bajta.
* ''read(6)'' wczytuje 6 bajtow z pliku.
* Klasa ''File'' posiada wiele przydatnych metod do szybkiej obslugi plikow:
* ''data = File.read(filename)'' otwiera plik, uzywa metody ''read'' i zamyka go.
* ''array_of_lines = File.readlines(filename)''.
* ''pos'' pokazuje, w ktorym miejscu w pliku sie znajdujemy.
* ''pos='' umieszcza wskaznik w danym miejscu (''f.pos = 8'')
# writing.rb
File.open("test.txt", "w") do |f|
f.puts "This is a test"
end
Output:
sqbell@sqbell-gentoo ~/ruby/beginning_ruby/ch09_files_db $ cat test.txt
This is a test
^ Tryby ^ Wlasnosci ^
| ''r'' | Tylko do odczytu, wskaznik na poczatku pliku |
| ''r+'' | Odczyt i zapis, wskaznik na poczatku pliku |
| ''w'' | Tylko zapis, nowy plik jest tworzony (istniejace sa zastepowane) |
| ''w+'' | Zapis i odczyt, ''File.new'' tworzy nowy plik (istniejacy jest zastepowany) |
| ''a'' | Zapis w trybie dolaczania do pliku, wskaznik jest umieszczony na koncu pliku |
| ''a+'' | Zapis i odczyt w trybie dolaczania, wskaznik umieszczony jest na koncu pliku |
| ''b'' | Tryb binarny, do odczytu plikow nietekstowych |
# logging.rb
f = File.new("logfile", "a")
f.puts Time.now
f.close
Output:
sqbell@sqbell-gentoo ~/ruby/beginning_ruby/ch09_files_db $ cat logfile
2011-05-06 13:10:29 +0200
2011-05-06 13:10:30 +0200
2011-05-06 13:10:31 +0200
2011-05-06 13:10:32 +0200
* ''File.rename("file1", "file2")'' zmienia nazwe pliku.
* ''File.delete("file")'' lub ''File.unlink("file")'' usuwa plik.
* ''File.join('full', 'path', 'to', 'a', 'file.txt')'' generuje sciezke dostepu.
* ''File.expand_path("file")'' wyswietli absolutna sciezke do pliku.
* ''puts "It exists!" if File.exist?("file")'' sprawdzi czy plik istnieje.
* ''puts File.size("file")'' wyswietli rozmiar pliku.
==== Blokady ====
W systemach UNIXowych mamy dwa rodzaje blokad: współdzielona (//shared//) i na wyłączność ((//exclusive//). Druga może być w posiadaniu tylko przez jeden proces. Procesy współdzielące plik muszą najpierw zwlonić blokadę, by proces, który chce posiadać plik na wyłączność będzie mógł taką założyć. Przykład w Rubym:
#!/usr/bin/env ruby -wKU
File.open("/tmp/foo", "w") do |f|
f.flock(File::LOCK_EX)
f.puts "Something clever..."
f.flock(File::LOCK_UN)
end
==== Katalogi ====
* ''Dir.chdir("/usr/bin")'' zmienia katalog,
* ''Dir.pwd'' wyswietla obecny katalog,
* ''Dir.entries("/usr/bin")'' zwroci tablice zawartosci katalogu,
* ''Dir.foreach("/usr/bin")'' dziala podobnie, ale jest iteratorem,
* ''Dir["/usr/bin/*"]'' wyswietli liste podkatalogow,
* ''%%Dir["**/**"]%%'' wyświetli wszystkie pliki i katalogi w obecnym katalogu, rekursywnie,
* ''Dir.mkdir("newdir")'' tworzy katalog,
* ''Dir.delete("newdir")'' usuwa katalog, podobnie jak ''Dir.unlink'' i ''Dir.rmdir''.
=== Tworzenie plikow tymczasowych ===
Wiekszosc systemow operacyjnych posiada katalogi, w ktorych programy moga przechowywac tymczasowe dane.
# tmp_dir.rb
require 'tmpdir'
puts Dir.tmpdir
tempfilename = File.join(Dir.tmpdir, "myapp.dat")
tempfile = File.new(tempfilename, "w")
tempfile.puts "This is only temporary."
tempfile.close
File.delete(tempfilename)
# poprzez biblioteke tempfile
require 'tempfile'
f = Tempfile.new('myapp')
f.puts "Hello"
puts f.path
f.close
Output:
/tmp
/tmp/myapp20110506-5927-3wba79
==== Strony kodowe ====
Aby okreslic kodowanie wczytywanego pliku, dodajemy je do trybu pliku po dwukropku.
File.new("text.txt", "r:utf-8").each { |line| puts line }
Aby sprawdzic "zewnetrzne" kodowanie jakiegokolwiek obiektu I/O, skorzystac mozemy z metody ''external_encoding''.
# ext_encoding.rb
puts File.open("logfile", "r:utf-8").external_encoding
puts File.open("logfile", "r").external_encoding
Output:
UTF-8
UTF-8
Mozna transkodowac wejscie z jednego kodowania w drugie:
# transcoding.rb
File.open("text.txt", "r:utf-8:iso-8859-2") do |f|
puts f.external_encoding
first_line = f.gets
puts first_line.encoding
end
Output:
UTF-8
ISO-8859-2
Wlasnosc ta jest przydatna gdy chcemy w calym programie korzystac z jednego kodowania (na przyklad utf-8) ale pliki wejsciowe mamy w innym.
====== Podstawowe bazy danych ======
===== Bazy danych w plikach tekstowych ======
==== CSV ====
# csv.rb
require 'csv'
CSV.open('text.txt', 'r').each do |person|
puts person.inspect
end
Output:
["Fred Bloggs", "Manager", "Male", "45"]
["Laura Smith", "Cook", "Female", "23"]
["Debbie Watts", "Professor", "Female", "38"]
===== Przechowywanie obiektow i struktur danych =====
==== PStore ====
PStore umozliwia zapisywanie obiektow i struktur danych do pliku i ladowania ich z powrotem. Takie operacje zwane sa //object persistence// i opieraja sie na technice zwanej //marshalling//, gdzie struktury sa splaszczane.
# pstore.rb
class Person
attr_accessor :name, :job, :gender, :age
end
fred = Person.new
fred.name = "Fred Bloggs"
fred.age = 45
laura = Person.new
laura.name = "Laura Palmer"
laura.age = 23
require 'pstore'
store = PStore.new("storagefile")
store.transaction do
store[:people] ||= Array.new
store[:people] << fred
store[:people] << laura
end
* Wszelkie operacje wykonujemy w "transakcji", by zapobiec uszkodzeniu danych.
* Poprzez ''||='' upewniamy sie, ze element '':people'' znajduje sie w danej 'przechowalni'. Nastepnie dodajemy do niego freda i laure (''<<'') i konczymy transakcje.
# pstore_read.rb
class Person
attr_accessor :name, :job, :gender, :age
end
require 'pstore'
store = PStore.new("storagefile")
people = []
store.transaction do
people = store[:people]
end
people.each do |person|
puts person.name
end
Output:
Fred Bloggs
Laura Palmer
==== YAML ====
# yaml.rb
require 'yaml'
class Person
attr_accessor :name, :age
end
fred = Person.new
fred.name = "Fred Bloggs"
fred.age = 45
laura = Person.new
laura.name = "Laura Smith"
laura.age = 23
test_data = [ fred, laura ]
puts YAML::dump(test_data)
Output:
---
- !ruby/object:Person
age: 45
name: Fred Bloggs
- !ruby/object:Person
age: 23
name: Laura Smith
Wczytywanie:
# yaml_read.rb
require 'yaml'
class Person
attr_accessor :name, :age
end
yaml_string = <
Output:
Fred Bloggs
Laura Smith
===== Relacyjne bazy danych i SQL =====
==== SQLite ====
# sqlite.rb
require 'rubygems'
require 'sqlite3'
$db = SQLite3::Database.new("dbfile") # $ - zmienna globalna
$db.results_as_hash = true # wymusza zwracanie wynikow jako hasha
def disconnect_and_quit
$db.close
puts "Bye!"
exit
end
def create_table
puts "Creating 'people' table"
$db.execute %q{CREATE TABLE people (
id integer primary key,
name varchar(50),
job varchar(50),
gender varchar(6),
age integer)
}
end
def add_person
puts "Enter name: "
name = gets.chomp
puts "Enter job: "
job = gets.chomp
puts "Enter gender: "
gender = gets.chomp
puts "Enter age: "
age = gets.chomp
$db.execute("INSERT INTO people (name, job, gender, age) VALUES (?, ?, ?, ?)", name, job, gender, age)
end
def find_person
puts "Enter name or ID of person to find: "
id = gets.chomp
person = $db.execute("SELECT * FROM people WHERE name = ? OR id = ?", id, id.to_i).first
unless person
puts "No result found."
return
end
puts %Q{Name: #{person['name']}
Job: #{person['job']}
Gender: #{person['gender']}
Age: #{person['age']}
}
end
==== MySQL ====
# mysql.rb
require 'rubygems'
require 'mysql'
db = Mysql.connect('localhost', 'root', 'test')
db.query("INSERT INTO people (name, age) VALUES('Chris', 25)")
begin
query = db.query('SELECT * FROM people')
puts "There were #{query.num_rows} rows returned."
query.each_hash do |h|
puts h.inspect
end
rescue
puts db.errno
puts db.error
end
db.close
==== DBI ====
DBI (The DataBase Interface) to proba utworzenia jednego interfejsu do porozumiewania sie z roznymi bazami danych.
# dbi.rb
require 'dbi'
db = DBI.connect('DBI:Mysql:db_name', 'username', 'password')
db.do("INSERT INTO people (name, age) VALUES (?, ?)", name, age)
query = db.prepare('SELECT * FROM people')
query.execute
while row = query.fetch do
puts row.inspect
end
query.finish
db.select_all('SELECT * FROM people') do |row|
puts row.inspect
end