User Tools

Site Tools


inf:ruby:rozdz_9

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 = <<END_OF_DATA
--- 
- !ruby/object:Person 
  age: 45
  name: Fred Bloggs
- !ruby/object:Person 
  age: 23
  name: Laura Smith
END_OF_DATA
 
test_data = YAML::load(yaml_string)
puts test_data[0].name
puts test_data[1].name

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
inf/ruby/rozdz_9.txt · Last modified: 2021/02/16 09:56 (external edit)