Programmering i Ruby

Den Pragmatiske Programmerers Veiledning

Forrige < Innhold ^
Neste >

Grunnleggende I/O



Ruby gir hva som ved første øyekast ser ut som to separate sett av I/O-rutiner. Det første er det enkle brukergrensesnittet---vi har brukt det nokså utelukkende så langt.

print "Enter your name: "
name = gets

Det er et helt sett av I/O-relaterte metoder implementert i Kernel module---gets, open, print, printf, putc, puts, readline, readlines, og test---som gjør det enkelt og greitt å skrive ukompliserte Ruby-programmer. Disse metodene gjør typisk I/O til standard input og standard output, som gjør dem godt egnet til å skrive filtere. Du vil finne dem dokumentert fra side 411.

Den andre måten som gir deg en del mer kontroll, er å bruke IO- objekter.

Hva er et IO-objekt?

Ruby definerer en enkel base-klasse, IO, for å håndtere input og output. Denne base-klassen er subklasset av klassene File og BasicSocket for å produsere mer spesialisert oppførsel, men prinsippene er de samme . Et IO-objekt er en toveis kommunikasjonskanal mellom et Ruby-program og en ekstern resssurs. [For de som bare må kjenne implementasjonsdetaljene, betyr det at et enkelt IO-objekt kan noen ganger styrer mer enn en operativsystem-deskriptor. For eksempel, hvis du åpner et par pipes, inneholder et IO-objekt både en lese-pipe og skrive-pipe ] Det kan være mer ved et IO-objekt en det som øyet ser, men rent praktisk tar du ganske enkelt og skriver til og leser fra det.

I dette kapittelet vil vi konsentrere oss om klassen IO og den mest vanlige brukte subklassen, File-klassen. For mer detaljer om bruk av socket-klassene for nettverk, se seksjonen som begynner på side 469.

Åpning og lukking av filer

Som du kanskje regner med kan du lage et nytt fil-objekt ved å bruke File.new .

aFile = File.new("testfile", "r")

# ... prosesser filen

aFile.close

Du kan lage et File-objekt som er åpent for lesing, skriving, eller begge deler, basert på modus-strengen (her åpnet vi "testfile" for lesing med "r"). En komplett liste over tillatte modus finnes på side 326. Du kan også spesifisere fil-rettigheter når du skaper en fil; se beskrivelsen av File.new på side 303 for detaljer. Etter å ha åpnet filen kan vi arbeide med den; skrive og/eller lese data etter behov. Til slutt, som ansvarlige programvare-innbyggere, lukker vi filen, og forsikrer oss at all buffrede data er skrevet og at alle relaterte ressurser er frigjort.

Men her kan Ruby gjøre livet ditt litt enklere. Metoden File.open åpner også en fil. I vanlig bruk oppfører den seg akkurat som File.new . Men dersom det er en blokk assosiert med kallet, vil open gjøre en vri. Istedet for å returnere et nytt File-objekt, kjører den blokken og sender det nylig åpnede File-objektet inn som en parameter. Når blokken avslutter, blir filen automatisk lukket.

File.open("testfile", "r") do |aFile|

# ... prosesser filen

end

Lese og skrive filer

De samme metodene som vi har benyttet for "enkel" I/O er tilgjengelig for alle fil-objekter. Så gets leser en linje fra standard input, og aFile.gets leser en linje fra fil-objektet. aFile.

Imidlertid så har også I/O-objekter et utvidet sett av tilgangsmetoder, alle med hensikt å gjøre våre liv enklere.

Iteratorer for lesing

I tillegg til å bruke vanlige løkker til å lese data fra en IO-strøm, kan du også benytte forskjellige Ruby-iteratorer. IO#each_byte kaller en blokk med den neste 8-bits byten fra IO-objektet (i dette tilfellet, et File-objekt).

aFile = File.new("testfile")
aFile.each_byte {|ch| putc ch; putc ?. }
produserer:
T.h.i.s. .i.s. .l.i.n.e. .o.n.e.
.T.h.i.s. .i.s. .l.i.n.e. .t.w.o.
.T.h.i.s. .i.s. .l.i.n.e. .t.h.r.e.e.
.A.n.d. .s.o. .o.n.......
.

IO#each_line kaller blokken med den neste linjen fra filen. I det neste eksemplet vil vi lage de opprinnelige linjeskiftene synlige ved å bruke String#dump , slik at du kan se at vi ikke lurer deg.

aFile.each_line {|line| puts "Got #{line.dump}" }
produserer:
Got "This is line one\n"
Got "This is line two\n"
Got "This is line three\n"
Got "And so on...\n"

Du kan sende each_line enhver tegnsekvens som skal brukes som linje-separator, og metoden vil bryte opp innputen ifølge det, og returnerer linjen slik at den ender ved hver linjes dataslutt. Det er derfor du ser ``\n''-tegnene i utputen fra det forrige eksempelet. I det neste eksempelet skal vi bruke ``e'' som linje-separator.

aFile.each_line("e") do |line|
  puts "Got #{ line.dump }"
end
produserer:
Got "This is line"
Got " one"
Got "\nThis is line"
Got " two\nThis is line"
Got " thre"
Got "e"
Got "\nAnd so on...\n"

Hvis du kombinerer en iterator med bruken av en blokk for automatisk lukking, vil du få IO.foreach . Denne metoden tar navnet til en I/O-kilde, åpner den for lesing, kaller iteratoren en gang for hver linje i filen, og så lukker filen automatisk.

IO.foreach("testfile") { |line| puts line }
produserer:
This is line one
This is line two
This is line three
And so on...

Eller du kan få tak i hele innholdet til en fil som en Array av linjene, hvis du foretrekker det:

arr = IO.readlines("testfile")
arr.length » 4
arr[0] » "This is line one\n"

Ikke glem at I/O er aldri sikkert i en usikker verden---unntak vil bli fremmet ved de fleste feil, og du bør være beredt til å fange dem og håndtere dem.

Skrive til fil

Så langt har vi gladelig kallet puts and print, sendt inn hvilket som helst objekt og stolt på at Ruby gjører det riktige (som det selvfølgelig gjør). Men hva ekstakt er det Ruby gjør? ing?

Svaret er ganske enkelt. Med et par unntak, vil hvert objekt du sender til puts og print bli konvertert til en streng ved å kalle objektets to_s-metode. Hvis for en eller annen grunn to_s-metoden ikke returnerer en gyldig streng, blir en streng laget som inneholder objektets klasse-navn og id, noe a la <ClassName:0x123456>.

Unntak er enkle også. nil-objektet vil skrive som strengen "nil", og en array som blir sendt til puts, vil bli skrevet som om hver av dens elementer ble sendt separat til puts.

Hva hvis du vil skrive ut binære data og ikke vil at Ruby skal rote med dem? Vel, normalt kan du ganske enkelt bruke IO#print og sende in en streng som inneholder bytene som skal skrives. Imidlertid kan du få tak i lav-nivå input- og output-rutiner hvis du virkelig vil---se på dokumentasjonen for IO#sysread og IO#syswrite på side 335.

Og hva gjør du for å få binære data inn i en streng i utgangspunktet? De to vanligste måtene er å legge inn byte etter byte, eller å bruke Array#pack .

str = "" » ""
str << 1 << 2 << 3 » "\001\002\003"
[ 4, 5, 6 ].pack("c*") » "\004\005\006"

Men jeg savner C++ sin iostream

Noen ganger er det smaken som teller... Imidlertid, akkurat som du kan legge til et objekt til en Array ved å bruke <<-operatoren, kan du også utputte et objekt til en IO-strøm:

endl = "\n"
$stdout << 99 << " red balloons" << endl
produserer:
99 red balloons

Nok en gang bruker <<-metoden to_s for å konvertere dens argumeter til strenger før den sender dem videre på deres ferd.

Kommunikasjon med Nettverk

Ruby snakker flytende i de meste av internett-protokollene, både lav-nivå og høy-nivå.

For dere som like å kravle rundt på nettverks-nivå, så kommer Ruby med et sett av klasser for socket-biblioteket (dokumentert fra side 469). Disse klassene gir deg tilgang til TCP, UDP, SOCKS, og Unix domene-sockets, så vel som andre socket-typer som er støttet av din arkitektur. Biblioteket tilbyr også hjelper-klasser for å gjøre laging av servere raskere. Her er et enkelt program som får informasjon om "Oracle"-brukeren på din lokale maskin ved å bruker finger-protokollen.

require 'socket'
client = TCPSocket.open('localhost', 'finger')
client.send("oracle\n", 0)    # 0 betyr standard pakke
puts client.readlines
client.close
produserer:
Login: oracle         			Name: Oracle installation
Directory: /home/oracle             	Shell: /bin/bash
Never logged in.
No Mail.
No Plan.

På et høyere nivå tilbyr lib/net samlingen av biblioteks-moduler håndtering av et sett av applikasjons-nivå-protokoller (for tiden FTP, POP, SMTP og telnet). Disse er dokumentert fra side 482. For eksempel, det følgende programmet lister opp bildene som blir vist på Pragmatic Programmers hjemmeside.

require 'net/http'

h = Net::HTTP.new('www.pragmaticprogrammer.com', 80) resp, data = h.get('/index.html', nil) if resp.message == "OK"   data.scan(/<img src="(.*?)"/) { |x| puts x } end
produserer:
images/title_main.gif
images/dot.gif
images/dot.gif
images/dot.gif
images/AgileAllianceSmall.jpg
images/dot.gif
images/dot.gif

( In progress translation to Norwegian by NorwayRUG. $Revision: 1.9 $ )
$Log: tut_io.xml,v $
Revision 1.9  2003/08/31 11:58:58  kent
Fjerner masse KapitaliseringsMani i overskrifter.

Revision 1.8  2002/07/23 13:47:45  kent
En kjapp overgang med noen omskrivinger.


Forrige < Innhold ^
Neste >

Extracted from the book "Programming Ruby - The Pragmatic Programmer's Guide".
Translation to norwegian by Norway Ruby User Group.
Copyright for the english original authored by David Thomas and Andrew Hunt:
Copyright © 2001 Addison Wesley Longman, Inc.
This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at
http://www.opencontent.org/openpub/).

(Please note that the license for the original has changed from the above. The above is the license of the original version that was used as a foundation for the translation efforts.)

Copyright for the norwegian translation:
Copyright © 2002 Norway Ruby User Group.
This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at
http://www.opencontent.org/openpub/).
Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder.
Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.