Ruby'de bir dosyanın tamamını okumak için File.read("dosya.txt"), yazmak için File.write("dosya.txt", metin) yeterlidir. Daha fazla denetim istediğinizde File.open metodunu blokla kullanırsınız: blok bittiğinde dosya kendiliğinden kapanır. Büyük dosyaları satır satır işlemek için File.foreach ya da each_line, klasördeki dosyaları desenle bulmak için Dir.glob kullanılır.

File sınıfı IO sınıfından türer, bu yüzden read, write, readlines ve foreach gibi metotların ayrıntılı açıklaması IO belgesindedir. Aşağıdaki tüm örnekleri Ruby 4.0.9 ile geçici bir klasörde çalıştırdık. Ruby'ye yeniyseniz önce Ruby öğrenme rehberi sayfasına göz atabilirsiniz.

Dosyanın tamamını okumak ve yazmak

Dosyanın tamamını okumak ve yazmak

File.write dosyayı açar, veriyi yazar, kapatır ve yazılan bayt sayısını döndürür. Dosya yoksa oluşturulur, varsa içeriği silinip baştan yazılır. File.read ise dosyayı açıp tüm içeriği tek bir metin olarak döndürür:

ruby
File.write("notlar.txt", "Birinci satır\nİkinci satır\nÜçüncü satır\n") # => 48
icerik = File.read("notlar.txt")
icerik          # => "Birinci satır\nİkinci satır\nÜçüncü satır\n"
icerik.encoding # => #<Encoding:UTF-8>

File.exist?("notlar.txt") # => true
File.exist?("yok.txt")    # => false

Dönüş değeri olan 48, Türkçe karakterler iki bayt tuttuğu için karakter sayısından büyüktür. Okunan metnin kodlaması varsayılan dış kodlamaya göre belirlenir; biz Ruby'yi -E UTF-8 ile çalıştırdık. Kodlamayı açıkça seçmeyi aşağıda ayrı başlıkta anlatıyoruz.

File.open ile blok kullanımı

File.open blok almadan çağrılırsa File nesnesi döndürür ve dosyayı sizin kapatmanız gerekir. Blokla çağrıldığında dosya nesnesini bloğa verir, blok bitince dosyayı kapatır ve bloğun son değerini döndürür. Blok içinde hata çıksa bile dosya kapanır. Bu yüzden çoğu durumda blok biçimi tercih edilir:

ruby
File.open("notlar.txt") do |f|
  f.each_line(chomp: true) { |satir| puts satir }
end
# çıktı: Birinci satır
# çıktı: İkinci satır
# çıktı: Üçüncü satır

ilk = File.open("notlar.txt") { |f| f.gets }
ilk # => "Birinci satır\n"

Blokların genel çalışma mantığı için Ruby blok ve yield yazısına bakabilirsiniz.

Açık dosyaya yazmak: puts, print ve write

Yazma modunda açtığınız dosya nesnesine üç farklı yolla veri gönderebilirsiniz. puts her argümandan sonra satır sonu ekler, print eklemez, write ise verdiğiniz argümanları olduğu gibi yazar ve toplam bayt sayısını döndürür. Blok biçiminde bu dönüş değeri File.open çağrısının sonucu olur:

ruby
File.open("liste.txt", "w") do |f|
  f.puts "elma", "armut"
  f.print "kiraz"
  f.write "\n", "son"
end
File.read("liste.txt") # => "elma\narmut\nkiraz\nson"

File.open("liste.txt", "w") { |f| f.write("abc", "de") } # => 5

Çok sayıda küçük yazma işlemi yapacaksanız dosyayı bir kez açıp blok içinde yazmak, her satır için File.write çağırmaktan daha mantıklıdır; çünkü File.write her çağrıda dosyayı yeniden açıp kapatır.

Satır satır okuma: each_line, foreach ve readlines

Üç yöntem de satırları ayırır ama bellekte tuttukları veri farklıdır. File.readlines tüm satırları bir dizi olarak döndürür; dosya küçükse pratiktir. File.foreach ve IO#each_line satırları teker teker bloğa verir, böylece dosyanın tamamı aynı anda bellekte durmaz. chomp: true seçeneği satır sonundaki \n karakterini atar:

ruby
File.readlines("notlar.txt", chomp: true)
# => ["Birinci satır", "İkinci satır", "Üçüncü satır"]

File.foreach("notlar.txt").with_index(1) { |s, i| puts "#{i}: #{s}" }
# çıktı: 1: Birinci satır
# çıktı: 2: İkinci satır
# çıktı: 3: Üçüncü satır

File.foreach blok almadan çağrıldığında bir Enumerator döndürür; yukarıda with_index bu sayede çalışıyor. Ayrıntı için Ruby Enumerator ve lazy yazısına bakın.

Dosya açma modları

Dosya açma modları: Hayır, Evet, Hayır, Hayır

File.open ve File.new ikinci argüman olarak bir mod alır. Varsayılan mod "r" (yalnızca okuma) dur. File belgesindeki tabloları şöyle özetleyebiliriz:

ModVar olan dosyayı siler mi?OkumaYazma
rHayırHer yerden, baştan başlarHata
wEvetHataHer yere, baştan başlar
aHayırHataYalnızca sona
r+HayırHer yerdenHer yere
w+EvetHer yerdenHer yere
a+HayırHer yerdenYalnızca sona

"r" ve "r+" olmayan bir dosya için kullanılamaz. Moda ek harfler de eklenebilir: "b" ikili veri (dış kodlama ASCII-8BIT olur, Windows'ta satır sonu dönüşümü yapılmaz), "t" metin verisi, "x" ise dosya zaten varsa hata veren oluşturma modu. Hiçbiri verilmezse akış metin olarak açılır.

ruby
File.open("gunluk.log", "w") { |f| f.puts "ilk kayıt" }
File.open("gunluk.log", "a") { |f| f.puts "ikinci kayıt" }
File.read("gunluk.log") # => "ilk kayıt\nikinci kayıt\n"

File.write("gunluk.log", "üçüncü kayıt\n", mode: "a")
File.readlines("gunluk.log").size # => 3

begin
  File.open("olmayan.txt", "r")
rescue Errno::ENOENT => e
  puts "#{e.class}: #{e.message}"
end
# çıktı: Errno::ENOENT: No such file or directory @ rb_sysopen - olmayan.txt

begin
  File.open("gunluk.log", "wx")
rescue Errno::EEXIST => e
  puts "#{e.class}: #{e.message}"
end
# çıktı: Errno::EEXIST: File exists @ rb_sysopen - gunluk.log

Dosya ve klasör sorguları, yol işlemleri

File sınıfının sınıf metotları dosya sistemini sorgulamak ve yolları parçalamak için kullanılır. File.exist? herhangi bir girdi için, File.file? normal dosya, File.directory? klasör için true döner. File.join parçaları ayraçla birleştirir. Klasör oluşturma ve silme için standart kütüphanedeki FileUtils modülü işinizi kolaylaştırır:

ruby
require "fileutils"
FileUtils.mkdir_p("proje/lib/alt")
File.write("proje/lib/a.rb", "")
File.write("proje/lib/alt/b.rb", "")
File.write("proje/oku.md", "")

File.directory?("proje/lib")      # => true
File.file?("proje/oku.md")        # => true
File.basename("proje/lib/a.rb")   # => "a.rb"
File.basename("proje/lib/a.rb", ".rb") # => "a"
File.extname("proje/lib/a.rb")    # => ".rb"
File.dirname("proje/lib/a.rb")    # => "proje/lib"
File.join("proje", "lib", "a.rb") # => "proje/lib/a.rb"
Dir.children("proje").sort        # => ["lib", "oku.md"]

FileUtils.mkdir_p ara klasörleri de oluşturur. FileUtils.rm_rf ise bir klasörü içindekilerle birlikte siler ve hata vermez; bu yüzden yolu iki kez kontrol etmeden kullanmayın.

Dir.glob ile dosya bulmak

Dir.glob bir desen alır ve eşleşen girdilerin adlarını varsayılan olarak sıralı bir dizi halinde döndürür. Desenler düzenli ifade değildir; kabuk joker karakterlerine benzer kurallarla çalışır:

DesenAnlamı
*Nokta ile başlamayan herhangi bir ad parçası
**/Alt klasörlere özyinelemeli iner
?Tek bir karakter
[a-z]Kümedeki tek bir karakter, [^a-z] ile olumsuzlama
{rb,md}Seçeneklerden biri
\Sonraki özel karakteri kaçışlar
ruby
Dir.glob("proje/**/*.rb")          # => ["proje/lib/a.rb", "proje/lib/alt/b.rb"]
Dir.glob("*.rb", base: "proje/lib") # => ["a.rb"]

FileUtils.rm_rf("proje")
Dir.exist?("proje")                 # => false

base: verdiğinizde desen o klasöre göre çözülür ve sonuçlara klasör adı eklenmez. Nokta ile başlayan gizli dosyalar * ile eşleşmez; bunlar için belge File::FNM_DOTMATCH bayrağını ya da {*,.*} desenini öneriyor. sort: false ile sıralamayı kapatabilirsiniz.

Pathname: nesne yönelimli yollar

Pathname, bir dosya ya da klasörün adını temsil eder, dosyanın kendisini değil. Değiştirilemez bir nesnedir ve File, FileTest ile Dir ve FileUtils metotlarının bir kısmını tek arayüzde toplar. Ruby 4.0 NEWS dosyasına göre Pathname artık çekirdek sınıf; denememizde require yazmadan çalıştı:

ruby
yol = Pathname.new("proje") / "lib" / "ana.rb"
yol          # => #<Pathname:proje/lib/ana.rb>
yol.basename # => #<Pathname:ana.rb>
yol.extname  # => ".rb"
yol.dirname  # => #<Pathname:proje/lib>
yol.to_s     # => "proje/lib/ana.rb"

Pathname("notlar.txt").read.lines.first # => "Birinci satır\n"

Kodlama seçimi: Türkçe karakterler ve BOM

Okuduğunuz dosya UTF-8 değilse kodlamayı encoding: seçeneğiyle ya da mod metnine iki nokta ile ekleyerek belirtirsiniz. Tek kodlama verirseniz metin o kodlamayla etiketlenir; "dış:iç" biçiminde iki kodlama verirseniz okunan veri dış kodlamadan iç kodlamaya dönüştürülür. "BOM|UTF-8" ise dosyanın başındaki BOM işaretini algılayıp atar:

ruby
File.binwrite("latin.txt", "\xE7ay".b)  # ISO-8859-9 ile "çay"
File.read("latin.txt", encoding: "ISO-8859-9")       # => "\xE7ay"
File.read("latin.txt", encoding: "ISO-8859-9:UTF-8") # => "çay"
File.read("latin.txt", mode: "rb").encoding          # => #<Encoding:BINARY (ASCII-8BIT)>

File.write("bomlu.txt", "\uFEFFmerhaba")
File.read("bomlu.txt").bytesize                  # => 10
File.read("bomlu.txt", mode: "r:BOM|UTF-8")      # => "merhaba"

İlk okumada çıktı "\xE7ay" görünür, çünkü Ruby baytı ISO-8859-9 olarak etiketler ama dönüştürmez. Kodlama konusunun tamamı için Ruby encoding ve Türkçe karakter yazısına bakın.

Ruby 4.0 ile değişen bir davranış

NEWS dosyasına göre IO sınıf metotları ve Kernel#open, başında | olan bir adla artık süreç başlatmıyor; bu kullanımdan kaldırılmış davranış 4.0 ile tamamen silindi. Böyle bir ad artık sıradan bir dosya yolu gibi ele alınır. Windows üzerindeki denememizde şu hata alındı:

ruby
begin
  File.read("|echo merhaba")
rescue => e
  puts "#{e.class}: #{e.message}"
end
# çıktı: Errno::EINVAL: Invalid argument @ rb_sysopen - |echo merhaba

Komut çalıştırmanız gerekiyorsa IO belgesinde anlatılan IO.popen metodunu kullanın. Linux üzerinde hata sınıfı farklı olabilir; önemli olan, komutun artık çalışmamasıdır.

Sık yapılan hatalar

Sık yapılan hatalar: Blok kullanmadan File.open çağırıp close unutmak. Kapatılmış bir dosyadan..., Ekleme yapmak isterken "w" kullanıp dosyayı silmek., Çok büyük bir log dosyasını...
  • Blok kullanmadan File.open çağırıp close unutmak. Kapatılmış bir dosyadan okumak ise IOError: closed stream verir.
  • Ekleme yapmak isterken "w" kullanıp dosyayı silmek.
  • Çok büyük bir log dosyasını File.read ile belleğe almak; satır satır işlemek için File.foreach daha uygundur.
  • Windows'ta ikili dosyayı metin modunda okumak. IO belgesi, metin modunun Windows'ta bazı özel baytlarda okumayı erken bitirebileceğini belirtiyor; tüm baytlar için File.binread önerilir.
  • Kodlamayı belirtmeden farklı kodlamalı bir dosyayı okuyup Türkçe karakterleri bozuk görmek.

Sık Sorulan Sorular

File.read ile IO.read arasında fark var mı?

File, IO sınıfının alt sınıfı olduğu için File.read aslında IO'nun sınıf metodunu kullanır. Dosya yolu verdiğiniz sürece ikisi aynı işi yapar.

Dosya var mı diye nasıl kontrol ederim?

File.exist?(yol) kullanın. Yalnızca normal dosya mı diye bakmak için File.file?, klasör için File.directory? ya da Dir.exist? uygundur.

Bir klasördeki tüm .rb dosyalarını nasıl listelerim?

Sadece o klasör için Dir.glob("klasor/*.rb"), alt klasörler dahil Dir.glob("klasor/**/*.rb") yazın.

CSV dosyalarını nasıl okurum?

CSV için ayrı bir kütüphane vardır; NEWS'e göre csv Ruby 4.0'da paketlenmiş gem olarak geliyor. Format kurallarını CSV formatı ve JSON dönüşümü yazısında anlattık; JSON dosyaları için Ruby JSON kullanımı yazısına bakabilirsiniz.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Ruby öğrenme rehberi · Ruby JSON kullanımı · Ruby encoding ve UTF-8 · Ruby hata yönetimi · Ruby 4.0 yenilikleri · Linux nedir?