Ders 12 / 12
Dosya ve Girdi/Çıktı
File.open çağrısında sonucu bloğun, dosyanın kapanmasını ise yöntemin belirlediği ölçülür; blok istisna fırlatsa da dosya kapanır. Ders, kursun on iki gözleminin kapanış tablosunu da verir.
Yayın Güncelleme
Bu derste
- File.read, File.readlines ve File.foreach ile dosyadan okur, File.write ile yazar
- Bloklu ve bloksuz File.open çağrısının dönüş değerini ve closed? durumunu ayırt eder
- Standart girdiyi bir borudan besleyip satır satır okur
- Blok istisna fırlatsa da dosyanın kapandığını ölçer ve kaynak temizlemeyi bloğun değil yöntemin üstlendiğini söyler
İçindekiler
Önceki dersin sonunda ensure her durumda koşan bir dal olarak görüldü. Dosyalar bu yapının en sık kullanıldığı yerdir: açılan bir dosya kapatılmak zorundadır ve kapatmayı unutmak bir sızıntıdır. Ruby’de File.open çağrısına bir blok verilebilir; soru, kapatma işinin kime ait olduğudur. Girdi/çıktı (input/output), programın dış dünyayla, yani dosyalarla ve standart akışlarla veri alışverişidir.
Bu dersin yanıtlayanı bloktur: File.open iletisi aynı dosyaya iki farklı blokla iki farklı sonuç verir. Dersin sonunda kursun on iki gözleminin tamamı tek tabloda toplanır.
Dosya Okuma Gözlemi
Çekirdeğin on ikinci gözlemi dosya okuma’dır. Sınıflandırma yardımcısı yanit.rb kursun başından beri aynıdır ve burada yalnız diske yazılır.
# yanit.rb — bir iletiyi kimin yanitladigini siniflandiran yardimcilar
# Siniflandirma sirayla sorar: once ifade bir ileti mi (defined?),
# sonra iki farkli blok iki farkli sonuc veriyor mu, sonra yontemin sahibi kim.
def yanitlayan(tanim, sahip: nil, sonuc: nil, bloklu: nil)
return ["sozdizimi", "defined? #{tanim}"] unless tanim == "method"
if bloklu
birinci, ikinci = bloklu.call
return ["blok", "#{birinci.inspect} / #{ikinci.inspect}"] unless birinci == ikinci
end
tur = sahip.instance_of?(Class) ? "sinif" : "modul"
[tur, "#{sahip}, sonuc #{sonuc.inspect}"]
end
# dosya_okuma.rb — ayni File.open iki blokla iki sonuc verir
require_relative "yanit"
File.write("not.txt", "ilk satir\nikinci satir\n")
tanim = defined?(File.open("not.txt"))
tur, kanit = yanitlayan(tanim, bloklu: -> {
[File.open("not.txt") { |f| f.gets.chomp }, File.open("not.txt") { |f| f.readlines.size }]
})
puts tanim
puts "#{tur} #{kanit}"
puts File.method(:open).owner.instance_of?(Class)
method blok "ilk satir" / 2 true
File.write iki satırlık not.txt dosyasını yazdı. defined? bloksuz çağrıyı "method" olarak bildirdi, yani ortada bir ileti var. İlk blok ilk satırı, ikinci blok satır sayısını döndürdü: aynı ileti, "ilk satir" ve 2. Yanıtlayan bloktur. File.open’ın sahibi bir sınıftır (instance_of?(Class) true); sahibin tekil sınıf olarak görünmesi ikinci kursun konusudur ve burada yalnız sonucu okunur. KB56. Ölçüm tek bir dosya ve iki blokla yapıldı.
Satır Okuma ve Yazma Akışları
Bloksuz kısa yollar, dosyayı açıp kapatma işini kendileri yapar. File.write yazılan bayt sayısını döndürür; File.readlines satırları bir dizi yapar ve chomp: true satır sonunu atar; File.foreach satırları tek tek verir. Olmayan bir dosya Errno::ENOENT fırlatır. Hata iletisi yol taşıdığı için yalnız sınıf basılır.
# akislar.rb — yazma, okuma ve eksik dosya
File.write("not.txt", "ilk satir\nikinci satir\n")
puts File.write("b.txt", "abc")
puts File.readlines("not.txt", chomp: true).inspect
puts File.foreach("not.txt").count
begin
File.read("yok.txt")
rescue Errno::ENOENT => e
puts e.class
end
3 ["ilk satir", "ikinci satir"] 2 Errno::ENOENT
abc üç bayttır ve File.write 3 döndürdü. readlines iki dizgiden oluşan bir dizi verdi. foreach bloksuz çağrıldığında bir yineleyici döndürür; count onu tüketip 2 buldu. Eksik dosya Errno::ENOENT fırlattı ve dal onu yakaladı. KB57. File.write yalnız dizgi içerikle ölçüldü.
Açık bir dosyayla çalışırken satır okuma yöntemleri örneğin üzerinde çağrılır. Bunların sahibi de ölçülebilir.
# sahipler.rb — dosya ve standart cikti yontemlerinin sahibi
File.write("not.txt", "ilk satir\nikinci satir\n")
File.open("not.txt") do |f|
puts f.class
puts f.method(:gets).owner
puts f.method(:each_line).owner
end
puts $stdout.method(:puts).owner
File IO IO IO
Dosya nesnesi bir File‘dır, ama gets ve each_line onun kendi sınıfında değil IO sınıfında tanımlıdır. $stdout.puts de aynı IO sınıfındadır. Dosya ile standart çıktı aynı sınıf ailesinin üyesidir ve bu üçünde de yanıtlayan sınıftır. KB58. Sahipler Ruby 4.0.7’de ölçüldü; hangi yöntemin hangi sınıfta tanımlı olduğu sürümden sürüme taşınabilir.
Açık Kalan ve Kapanan Dosya
File.open bloksuz çağrılırsa dosya nesnesini döndürür ve dosya açık kalır; kapatmak çağıranın işidir. Bloklu çağrıda ise dosya nesnesi bloğa verilir, blok bitince dosya kapanır ve çağrının değeri bloğun son ifadesidir.
# kapanma.rb — bloksuz ve bloklu File.open
File.write("not.txt", "ilk satir\nikinci satir\n")
f = File.open("not.txt")
puts f.class
puts f.closed?
f.close
puts f.closed?
tutulan = nil
deger = File.open("not.txt") do |dosya|
tutulan = dosya
puts dosya.closed?
42
end
puts deger
puts tutulan.closed?
File false true false 42 true
Bloksuz çağrı File döndürdü, closed? false çıktı ve ancak elle close çağrılınca true oldu. Bloklu çağrıda dosya blok içinde açıktı. Blok 42 ile bitti: dönüş değeri 42 oldu, blok dışına taşınan dosya nesnesi ise artık kapalıydı. Sonuç bloktan, kapanma yöntemden gelir. KB59. Ölçüm yerel dosya ve tek bir blok için yapıldı.
Açma Kipleri
File.open ikinci argüman olarak bir kip alır. "w" dosyayı açarken sıfırlar, "a" mevcut içeriğin sonuna ekler. Fark satır sayısıyla ölçülür. Bloklu File.open çağrısının değeri bloğun son ifadesidir; write yazılan bayt sayısını verir.
# kipler.rb — w sifirlar, a sonuna ekler
File.write("kip.txt", "bir\niki\n")
File.open("kip.txt", "a") { |f| f.puts "uc" }
puts File.readlines("kip.txt").size
donus = File.open("kip.txt", "w") { |f| f.write("tek\n") }
puts donus
puts File.readlines("kip.txt").size
File.open("kip.txt", "a") { |f| puts f.method(:puts).owner }
3 4 1 IO
İki satırlık dosyaya "a" ile bir satır eklenince sayı 3 oldu. "w" ile açılış dosyayı sıfırladı: blok write değerini, yani 4 baytı döndürdü ve satır sayısı 1’e indi. puts yöntemi IO sınıfında tanımlıdır; yanıtlayan sınıftır. KB60. Ölçüm yalnız "a" ve "w" kipleriyle yapıldı; okuma ve yazma kipleri birlikte denenmedi.
puts ve print Farkı
Dosyaya yazarken puts satır sonu ekler, print eklemez. Fark, yazılan içeriğin inspect çıktısında görünür.
# yazim_farki.rb — puts satir sonu ekler, print eklemez
File.open("fark.txt", "w") do |f|
f.puts "a"
f.print "b"
f.puts "c"
end
puts File.read("fark.txt").inspect
puts File.size("fark.txt")
File.open("fark.txt") { |f| puts f.method(:print).owner }
"a\nbc\n" 5 IO
Dosyada a, satır sonu, b, c ve yine satır sonu var; b ile c bitişik yazıldı. Toplam beş bayt. print de IO sınıfındadır; yanıtlayan sınıftır. KB61. İçerik ASCII dizgilerdi; çok baytlı karakterlerde bayt sayısı karakter sayısından ayrışır ve burada ölçülmedi.
Var Olma ve Boyut
File.exist? bir yolun var olup olmadığını, File.size bayt boyutunu verir. Açık bir dosya nesnesinin kendi size yöntemi de vardır ve sahibi File’dır.
# boyut.rb — var olma ve boyut
File.write("boyut.txt", "abcd")
puts File.exist?("boyut.txt")
puts File.exist?("yok.txt")
puts File.size("boyut.txt")
File.open("boyut.txt") do |f|
puts f.size
puts f.method(:size).owner
end
true false 4 4 File
Var olan dosya için true, olmayan için false döndü. Boyut 4 bayttı ve açık nesne de aynı değeri verdi. Bu kez sahip IO değil File’dır: dosyaya özgü bilgi alt sınıfta durur, akış işleri üst sınıfta. KB62. Boş dosya ve silinmiş dosya ölçülmedi.
Satır Satır ve Enumerable
each_line blok verilmezse bir yineleyici döndürür ve chomp: true satır sonlarını atar. Yineleyicinin map yöntemi Enumerable modülünden gelir. Okuma, dönüştürme, süzme ve yazma bir akış olarak bağlanabilir.
# akis.rb — oku, map, select, yaz
File.write("girdi.txt", "elma\nkar\narmut\nay\n")
satirlar = []
File.open("girdi.txt") { |f| f.each_line(chomp: true) { |s| satirlar << s } }
puts satirlar.inspect
yineleyici = File.foreach("girdi.txt", chomp: true)
puts yineleyici.method(:map).owner
sonuc = yineleyici.map(&:upcase).select { |s| s.size > 3 }
yazilan = File.write("cikti.txt", sonuc.join("\n") + "\n")
puts yazilan
puts File.read("cikti.txt").inspect
["elma", "kar", "armut", "ay"] Enumerable 11 "ELMA\nARMUT\n"
each_line dört satırı sondaki satır sonu olmadan verdi. File.foreach blok almadan çağrılınca bir yineleyici döndürdü ve map onun için Enumerable modülündedir; yanıtlayan modüldür. upcase sonrası select dört harften uzun olanları bıraktı: ELMA ve ARMUT. Yazılan ELMA, satır sonu, ARMUT, satır sonu on bir bayttır. KB63. Akış tek geçişli ve küçük bir girdiyle ölçüldü; büyük dosyada belleğe ne olduğu ölçülmedi.
Standart Girdiyi Borudan Okumak
Standart girdi, programın dışarıdan satır aldığı akıştır ve $stdin ile okunur. Ders blokları etkileşimli girdi beklemez; bu yüzden girdi bir boru (pipe) ile verilir: bir komutun çıktısı, sonraki komutun girdisi olur.
# oku.rb — standart girdiyi satir satir okur ilk = $stdin.gets.chomp kalan = $stdin.each_line.map(&:chomp) puts ilk puts kalan.inspect puts $stdin.method(:gets).owner
# girdi.sh — oku.rb'yi borudan besler printf 'bir\niki\nuc\n' | ruby oku.rb
bir ["iki", "uc"] IO
gets ilk satırı okudu, each_line kalanları tüketti. $stdin.gets de IO sınıfındadır; dosyayı da klavyeyi de aynı sınıf okur. KB64. Girdi üç satırlık sabit bir dizgiydi; boş girdi ve çok büyük girdi ölçülmedi.
Aynı boru satır saymak için de kullanılır. each_line bloksuz çağrılınca bir yineleyici döndürür; count ona Enumerable modülünden gelir.
# say.rb — standart girdideki satirlari sayar puts $stdin.each_line.count puts $stdin.method(:each_line).owner puts $stdin.each_line.method(:count).owner
# say.sh — say.rb'yi borudan besler printf 'a\nb\nc\nd\n' | ruby say.rb
4 IO Enumerable
Dört satırlık girdi 4 olarak sayıldı. each_line yöntemi IO sınıfındadır, yineleyicinin count yöntemi ise Enumerable modülündedir; bu iki yanıtlayan aynı ifadede art arda çalışır. KB65. Girdi sabit ve dört satırlıktı; son satırı satır sonsuz biten girdi ölçülmedi.
Sınırlayıcı Ölçüm
Bu dersin yanıtlayanı blok, ama bu cümle yarım kalırsa yanıltır: blok sonucu belirler, kaynağı temizleyen blok değildir. Kapatmayı bloklu File.open yöntemi üstlenir. Bunu ayırmak için bloğun içinde bir istisna fırlatılır. Dosya nesnesi bloğun dışında tutulur, istisna yakalanır ve ardından dosyanın durumu sorulur. Karşılaştırma için aynı istisna bloksuz açılmış bir dosyanın yanında fırlatılır.
# istisna_kapanis.rb — blok istisna firlatsa da dosya kapanir
File.write("not.txt", "ilk satir\nikinci satir\n")
tutulan = nil
begin
File.open("not.txt") do |dosya|
tutulan = dosya
raise "blok koptu"
end
rescue => e
puts e.class
end
puts tutulan.closed?
elle = File.open("not.txt")
begin
raise "elle acik"
rescue => e
puts e.class
end
puts elle.closed?
elle.close
puts elle.closed?
RuntimeError true RuntimeError false true
İlk durumda blok RuntimeError fırlattı, dal onu yakaladı ve dosya yine de kapalıydı (true). İkinci durumda aynı istisna bloksuz açılmış dosyanın yanında fırlatıldı ve dosya açık kaldı (false) ta ki elle close çağrılana kadar. Fark bloğun içeriğinde değil, kapatma sorumluluğunun yerindedir: bloklu File.open, bloğu bir ensure içinde koşturur ve ne olursa olsun dosyayı kapatır. Bu, önceki dersteki kaynak temizleme yapısının dilin kitaplığına yerleştirilmiş biçimidir. Sonucu blok belirler, kaynak temizliğini File.open üstlenir. KB66. Ölçüm RuntimeError ile yapıldı; blokta break ya da throw ile çıkış denenmedi.
Özet
File.read,File.readlinesveFile.foreachdosyayı kendileri açıp kapatır;File.writeyazılan bayt sayısını döndürür; olmayan bir dosyayı okumakErrno::ENOENTfırlatır.- Bu dersin yanıtlayanı bloktur: aynı
File.openiletisi bir blokla"ilk satir", öteki blokla2verdi. - Bloksuz
File.opendosyayı açık bırakır; bloklu çağrıda blok bitinceclosed?trueolur ve dönüş değeri bloğun değeridir. gets,each_lineve$stdout.putsIOsınıfında tanımlıdır; standart girdi bir borudan beslenerek okunur."w"dosyayı sıfırlar,"a"sonuna ekler;putssatır sonu ekler,printeklemez; yineleyicininmapvecountyöntemleriEnumerablemodülündedir.- Sınırlayıcı ölçüm: blok istisna fırlatsa da dosya kapanır, çünkü kapatmayı blok değil
File.openyöntemi yapar.
Kurs Kapanışı
Kursun her dersi aynı soruyu sordu: bu iletiyi kim yanıtlıyor. Tablo, on iki dersin ölçtüğü iletiyi, o dersin yanıtlayanını ve sınırlayıcı ölçümünün söylediği şeyi yan yana koyar. Yanıtlayan sütunu her dersin ## Özet bölümünde yazılı olandır.
| Ders | Ölçülen ileti | Yanıtlayan | Sınırlayıcı ölçüm |
|---|---|---|---|
| Ruby’nin Tasarım Felsefesi | 5.between?(1, 10) |
modül | between? Comparable’da, <=> alıcının sınıfında |
| Nesne Modeli Sezgisi | 5 + 3 |
sınıf | Integer.=== iletisini Module yanıtlar |
| Değişkenler ve Sabitler | x = 5 |
sözdizimi | y += 1 atamadır, içindeki toplamayı Integer yanıtlar |
| Denetim Akışı | (1..10) === 7 |
sınıf | case, dal nesnesinin sınıfına sorar |
| Yöntemler | format("%03d", 7) |
modül | üst düzey def Object’e yazılır ve özeldir |
| Semboller ve Dizgiler | "ad".to_sym |
sınıf | sihirli yorum sahibi değiştirmez, dosya başına geçerlidir |
| Diziler ve Karma Yapılar | Hash.new { 0 }[:x] |
blok | blok yalnız anahtar yokken yanıtlar |
| Bloklar | sayilar.map { ... } |
blok | each iki blokla da [1, 2] verir, sahibi Array |
Enumerable Modülü |
(1..5).each_slice(2) |
modül | sum aralıkta modülde, dizide Array’de |
| Proc ve Lambda | ->(n) { n * 2 }.call(3) |
sınıf | sahip aynı, fark lambda? durumunda |
| İstisnalar | begin ... rescue ... end |
sözdizimi | dalın eşleşmesi === iletisiyle yapılır |
| Dosya ve Girdi/Çıktı | File.open("not.txt") { ... } |
blok | kapatmayı blok değil yöntem yapar |
On iki dersin yanıtlayanı dağılımı: 4 sınıf · 3 modül · 3 blok · 2 sözdizimi. Bu, çekirdeğin on iki gözleminin dağılımıyla aynıdır, çünkü çekirdek her ders için bir gözlem taşır. İki sonuç tablodan okunur. Birincisi, yanıtlayan çoğu zaman tek başına çalışmadı: sınırlayıcı ölçümlerin çoğu, dersin yanıtlayanının yanında başka bir yanıtlayanın da çalıştığını gösterdi. İkincisi, yanıtlayan sorusunun cevabı iletiye değil, iletinin alıcısına, verilen bloğa ve ifadenin biçimine bağlıdır. KB67. Tablo bu kursun on iki gözleminin dağılımını verir; Ruby’nin tamamının dağılımı hakkında bir iddia taşımaz.
Kurs burada kapanır. Ruby’de bir işlemin sonucunu üreten kod dört yerde durur: alıcının sınıfında, karıştırılmış bir modülde, çağıranın verdiği blokta ya da dilin kendi sözdiziminde. Bu kurs bu dört yeri yalnız sonucu ölçerek ayırdı; bir yöntemin neden belirli bir sınıfta ya da modülde bulunduğu sorusu açık kaldı. Sıradaki kursun konu alanı sınıf tanımı, kalıtım, modüller ve karışımlar, yöntem arama zinciri ve metaprogramlamadır; bu derslerde bugünkü ölçümlerin arkasındaki düzenek ele alınacaktır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.