FIGYELEM! Ez a dokumentum kizárólag az ELTE IK hallgatók számára oktatási célra készült! Félkész munka, dolgozunk rajta! Terjeszteni, felhasználni máshol a szerzők engedélye nélkül tilos! |
|
Alapvető be- és kimenet
A Ruby első ránézésre két különböző I/O rutinok halmazát nyújtja. Az első egy egyszerű felület - már eddig is elég sokszor használtuk.
print "Enter your name: "
|
|
name = gets
|
|
|
I/O metódusok teljes halmaza megtalálható a Kernel modulban - gets , open , printf , print , putc , puts , readline , readlines , és test -, melyek a nyílt Ruby programok írását teszik kényelmessé. Ezek a metódusok tipikusan az alapértelmezett be- és kimeneten végzik műveleteiket, ami szűrők írásához teszi őket hasznossá.
A másik mód - ami sokkal több irányítást ad a kezünkbe - az IO objektumok használata.
Mi is egy IO objektum?
A Ruby egy egyszerű, alap IO osztályt definiál a be- és kimeneti műveletek kezelésére. Ebből az osztályból további File és BasicSocket alosztályok származnak, melyek még specializáltabb viselkedést tesznek lehetővé, de az alapelvek ugyanazok. Egy IO objektum nem más, mint egy kétirányú csatorna egy Ruby program, és valamilyen külső erőforrás között. (Azok számára, akiknek mindeképp tudni kell az implementálás részleteit: ez azt jelenti, hogy egy egyszerű IO objektum néha több dolgot is kezelhet, mint az az operációs rendszer egy egyszerű file leírója (descriptor). Például, ha megnyitunk egy pár csatornát (pipe), egy egyszerű IO objektum tartalmaz egy olvasó csatornát, és egy író csatornát is.) Egy IO objektum több is lehet, mint aminek látszik, dehát végülis még mindig csak olvasunk róla, és írunk rá.
Ebben a fejezetben az IO osztályra koncentrálunk, valamint annak legtöbbször használt alosztályára: a File osztályra.
File-ok megnyitása, és bezárása
Mint ahogy az valószínüleg vártuk, egy új file objektumot a File.new metódussal hozhatunk létre.
aFile = File.new("testfile", "r")
|
|
# ... file feldolgozása
|
|
aFile.close
|
|
|
Egy file objektumot létrehozhatunk írásra, olvasásra, vagy mindkettőre a módot megadó stringtől függően. A példánkban a testfile-t olvasásra nyitottuk meg, ezt jelenti a második paraméterben szereplő r (read). A megnyitási módok teljes listáját itt nem részletezzük. Opcionálisan beállíthatjuk a hozzáférést is. A file megnyitása után már dolgozhatunk is vele. Végül, mint felelősségteljes szoftverfelhasználók, lezárjuk a file-t, biztosítva, hogy minden memóriatárban szereplő adat kiírásra kerül, és minden kapcsolódó erőforrást felszabadítunk.
De itt a Ruby egy picit könnyíthet a dolgon. A File.open metódus szintén megnyit egy file-t. Általános használatban ugyanúgy viselkedik, mint a File.new . Azonban, ha egy blokk is kapcsolódik a híváshoz, az open másként viselkedik. Egy új File objektum visszaadása helyett megnyitja a blokkot, átadva neki paraméterként a létrejött új objektumot. Amikor a blokk kilép, a file automatikusan lezáródik.
File.open("testfile", "r") do |aFile|
|
|
# ... file feldolgozása
|
|
end
|
|
|
File-ok írása, olvasása
Minden metódus, amit az egyszerű I/O-nál láttunk, elérhetők a file objektumokra is. Tehát gets beolvas egy sort az alapértelmezett bemenetről, míg aFile.gets beolvas egy sort az aFile objektumból.
Mindemellett az I/O objektumok egyéb elérhetőségi metódusokkal is rendelkeznek, hogy könnyebbé tegyük életünket e téren.
Iterátorok használata beolvasáshoz
Egy IO folyamról való adat leolvasásához használhatjuk a szokásos ciklusokat, vagy használhatjuk a számos Ruby iterátor egyikét. Az IO#each_byte hív egy blokkot az IO objektumról leolvasott 8 bites byte-tal (ebben az esetben a File objektumról).
aFile = File.new("testfile")
|
|
aFile.each_byte {|ch| putc ch; putc ?. }
|
|
|
Eredménye:
E.z. .a.z. .e.l.s.ő. .s.o.r.
|
|
.E.z. .a. .m.á.s.o.d.i.k.
|
|
.É.s. .í.g.y. .t.o.v.á.b.b.......
|
|
.
|
|
|
Az IO#each_line a file-ból beolvasott következő sorral hívja meg a blokkot. A következő példában az eredeti új sor karaktereket is láthatóvá tesszük a String#dump metódussal, szóval láthatjátok, hogy nem csalunk.
aFile.each_line {|line| puts "Got #{line.dump}" }
|
|
|
Eredménye:
Got "Ez az első sor\n"
|
|
Got "Ez a második\n"
|
|
Got "És így tovább...\n"
|
|
|
Az each_line -t átadhatjuk bármilyen karaktersorozatnak a sorok elválasztójaként, és ez aszerint tördeli a bemenetet, visszadva a sorvége jelet az adott adat minden sorának végén. Ezért látjuk a \n karaktereket az előző példa kimenetén. A következő példában az a karaktert használjuk a sorok elválasztására.
aFile.each_line("e") do |line|
|
|
puts "Got #{ line.dump }"
|
|
end
|
|
|
Eredménye:
Got "Ez a"
|
|
Got "z első sor\n Ez a"
|
|
Got " második\n És így tovább...\n"
|
|
|
Ha ötvözzük az iterátorok használatát az autómatikus lezáró blokkal, megkapjuk az IO.foreach metódust, amely egy I/O forrás nevét kapja forrásként, megnyitja olvasásra, meghívja az iterátort egyszer a file minden sorára, és automatikusan lezárja a file-t.
IO.foreach("testfile") { |line| puts line }
|
|
|
Eredménye:
Ez az első sor
|
|
Ez a második
|
|
És így tovább...
|
|
|
Vagy, ha így jobban tetszik, visszafejthetjük a file-t egy sorok tömbjébe:
arr = IO.readlines("testfile")
|
|
arr.length
|
» 4
|
arr[0]
|
» "Ez az első sor\n"
|
|
Ne felejtsük el, hogy az I/O sosem biztos egy bizonytalan világban: kivételek kiváltódnak a legtöbb hibánál, szóval legyünk készen ezek lekezelésére.
Írás file-ba
Ezidáig merészen hívogattuk a puts és print metódusokat különböző paraméterekkel, bízva benne, hogy a Ruby a helyes dolgot fogja cselekedni. De mit is csinál pontosan?
A válasz elég egyszerű. Néhány kivétellel, minden objektum, amit a puts és print műveleteknek átadunk, az adott objektum to_s metódusának használatával string-gé konvertálódik. Ha valamilyen okból a to_s érvénytelen stringet adna vissza, akkor egy olyan string jön létre, amely tartalmazza az objektum osztályának nevét, és azonosítóját, valahogy így: .
A kivételek is egyszerűek. A nil objektum a nil stringként kerül kiírásra. A puts minden tömböt elemenként dolgoz fel, mintha külön-külön kapta volna őket paraméterként.
Mi van, ha bináris adatot akarunk írni, és nem akarjuk, hogy a Ruby ezzel szöszmögjön? Normális körülmények között használhatjuk az IO#print metódust, paraméterként pedig a kiírandó byte-okat tartalmazó string-et. Habár megnézhetjük a be- és kimenet kezelésének alacsony szintű megvalósítását is. Javasoljuk az IO#sysread , IO#syswrite műveletek leírását.
Node, hogyan is rakjuk bele a bináris adatot egy string-be? A két általános megoldás a byte-onként beszúrás, vagy az Array#pack használata.
str = ""
|
» ""
|
str << 1 << 2 << 3
|
» "\001\002\003"
|
[ 4, 5, 6 ].pack("c*")
|
» "\004\005\006"
|
|
Hiányolom a C++ Iostreamet!
Nos, ez ízlés kérdése. Bár ahogy egy tömbhöz hozzá tudunk fűzni objektumot a << operátorral, úgy egy kimeneti folyammal is megtehetjük ugyanezt.
endl = "\n"
|
|
$stdout << 99 << " red balloons" << endl
|
|
|
Eredménye:
Ne felejtsük el, hogy a << operátor is először az objektumok to_s metódusát használja azok string-gé alakításához.
Párbeszéd hálózatokkal
A Ruby a legtöbb Internet protokollal könnyedén bánik, legyen az alacsony, vagy magas szintű.
Azoknak, akik szeretnek a hálózati szintekben túrkálni, a Ruby socket könyvtára egy sereg osztállyal szolgál ezen igények kielégítésére. Ezek az osztályok hozzáférést biztosítanak TCP, UDP, SOCKS és Unix domain socketekhez, valamint bármilyen további, a rendszerükben támogatott socket típusokhoz. A könyvtár a szerver-alkalmazások írásának megkönnyítésére számos segédosztályt is tartalmaz. Itt egy egyszerű program, amely információt gyűjt az oracle felhasználóról a finger protokoll használatával.
require 'socket'
|
|
client = TCPSocket.open('localhost', 'finger')
|
|
client.send("oracle\n", 0) # 0 jelentése normál socket
|
|
puts client.readlines
|
|
client.close
|
|
|
Eredménye:
Login: oracle Name: Oracle installation
|
|
Directory: /home/oracle Shell: /bin/bash
|
|
Never logged in.
|
|
No Mail.
|
|
No Plan.
|
|
|
Egy magasabb szinten a lib/net könyvtármodulok halmaza alkalmazás-szintű protokollok (FTP, HTTP, POP, SMTP, telnet) támogatását is tartalmazza. Például a következő program kilistázza a Pragmatic Programmer honlapon megjelenített képeket.
require 'net/http'
|
|
|
|
h = Net::HTTP.new('www.pragmaticprogrammer.com', 80)
|
|
r = h.get('/index.html', nil)
|
|
if r.message == "OK"
|
|
r.body.scan(/<img src="(.*?)"/) { |x| puts x }
|
|
end
|
|
|
Eredménye:
images/title_main.gif
|
|
images/dot.gif
|
|
images/dot.gif
|
|
images/dot.gif
|
|
images/aafounders_70.jpg
|
|
images/pp_cover_thumb.png
|
|
images/ruby_cover_thumb.png
|
|
images/dot.gif
|
|
images/dot.gif
|
|
|
|