Ruby'de bir dosyayı okumanın genel yolları nelerdir?


280

Ruby'de bir dosyayı okumanın genel yolları nelerdir?

Örneğin, işte bir yöntem:

fileObj = File.new($fileName, "r")
while (line = fileObj.gets)
  puts(line)
end
fileObj.close

Ruby'nin son derece esnek olduğunu biliyorum. Her yaklaşımın faydaları / dezavantajları nelerdir?


6
Mevcut kazanan cevabın doğru olduğunu düşünmüyorum.
inger

Yanıtlar:


259
File.open("my/file/path", "r") do |f|
  f.each_line do |line|
    puts line
  end
end
# File is closed automatically at end of block

Yukarıdaki gibi bir dosyayı açıkça kapatmak da mümkündür ( opensizin için kapatmak için bir blok geçirin):

f = File.open("my/file/path", "r")
f.each_line do |line|
  puts line
end
f.close

14
Bu neredeyse deyimsiz Ruby. Blok foreachyerine kullanın openve dağıtın each_line.
Tin Man

7
f.each { |line| ... }ve f.each_line { |line| ... }aynı davranışa sahip gibi görünüyorlar (en azından Ruby 2.0.0'da).
Ocak'ta chbrown

327

Dosya çok uzun değilse en kolay yol:

puts File.read(file_name)

Gerçekten, IO.readveya File.readotomatik olarak dosyayı kapatın, bu yüzden File.openbir blok ile kullanmaya gerek yoktur .


16
IO.readveya File.readdosyayı otomatik olarak kapatır, ancak ifadeleriniz göründüğü gibi ses çıkarmaz.
Phrogz

15
"dosya çok uzun değilse" dedi. Davam mükemmel bir şekilde uyuyor.
jayP

227

Dosyaları "slurping" konusunda dikkatli olun. İşte o zaman tüm dosyayı bir kerede belleğe okudunuz.

Sorun şu ki, iyi ölçeklenmiyor. Makul boyutta bir dosya ile kod geliştiriyor olabilirsiniz, daha sonra üretime sokabilir ve aniden gigabayt cinsinden ölçüm dosyalarını okumaya çalıştığınızı fark edebilirsiniz ve ana makineniz belleği okumaya ve ayırmaya çalışırken donuyor.

Satır satır G / Ç çok hızlıdır ve neredeyse her zaman bulamaç kadar etkilidir. Aslında şaşırtıcı derecede hızlı.

Kullanmayı seviyorum:

IO.foreach("testfile") {|x| print "GOT ", x }

veya

File.foreach('testfile') {|x| print "GOT", x }

Dosya IO'dan miras alır ve foreachIO'dadır, böylece her ikisini de kullanabilirsiniz.

read" Neden bir dosya slurping" iyi bir uygulama değil? "De satır satır I / O üzerinden büyük dosyaları okumaya çalışmanın etkisini gösteren bazı kriterler var .


6
Tam da aradığım şey buydu. Beş milyon satırlık bir dosyam var ve bunun belleğe yüklenmesini istemiyordum.
Scotty C.

68

Dosyayı bir kerede okuyabilirsiniz:

content = File.readlines 'file.txt'
content.each_with_index{|line, i| puts "#{i+1}: #{line}"}

Dosya büyükse veya büyük olabilirse, dosyayı satır satır işlemek genellikle daha iyidir:

File.foreach( 'file.txt' ) do |line|
  puts line
end

Bazen dosya tanıtıcısına erişmek veya okumaları kendiniz kontrol etmek istersiniz:

File.open( 'file.txt' ) do |f|
  loop do
    break if not line = f.gets
    puts "#{f.lineno}: #{line}"
  end
end

İkili dosyalarda, nil-separator ve bir blok boyutu belirtebilirsiniz, örneğin:

File.open('file.bin', 'rb') do |f|
  loop do
    break if not buf = f.gets(nil, 80)
    puts buf.unpack('H*')
  end
end

Son olarak, örneğin birden fazla dosyayı aynı anda işlerken blok olmadan yapabilirsiniz. Bu durumda, dosya açıkça kapatılmalıdır (@antinome yorumuna göre geliştirildi):

begin
  f = File.open 'file.txt'
  while line = f.gets
    puts line
  end
ensure
  f.close
end

Kaynaklar: File API ve IO API .


2
Hiçbir yoktur for_eachDosya veya IO. foreachBunun yerine kullanın .
Teneke Adam

1
Buradaki yanıtlarda kullanılacak kodu belgelerken genellikle Yüce Metin düzenleyicisini RubyMarkers eklentisiyle birlikte kullanırım. IRB kullanmaya benzer şekilde ara sonuçların gösterilmesini gerçekten kolaylaştırır. Ayrıca Sublime Text 2 için Seeing Is Believing eklentisi gerçekten güçlü.
Teneke Adam

1
Mükemmel cevap. Geçen Mesela ben kullanarak önerebiliriz whileyerine loopve kullanan ensurebir özel duruma neden olsa bile kapalı alır dosyayı sağlamak için. Bunun gibi (satırsonu ile noktalı virgül değiştirin): begin; f = File.open('testfile'); while line = f.gets; puts line; end; ensure; f.close; end.
antinome

1
evet bu çok daha iyi @antinome, cevabı geliştirdi. Teşekkürler!
Victor Klos

26

Basit bir yöntem kullanmaktır readlines:

my_array = IO.readlines('filename.txt')

Giriş dosyasındaki her satır dizideki bir giriş olacaktır. Yöntem dosyayı sizin için açıp kapatır.


5
Olduğu gibi readya da herhangi bir varyant, bu dosya kullanılabilir bellek büyükse büyük sorunlara yol açabilir, belleğe dosyanın tamamını çeker. Buna ek olarak, bir dizi olduğu için, Ruby diziyi oluşturmak zorunda ve işlemi de yavaşlatır.
Tin Man


9

Genellikle bunu yaparım:

open(path_in_string, &:read)

Bu size tüm metni bir dize nesnesi olarak verecektir. Sadece Ruby 1.9 altında çalışır.


Bu güzel ve kısa! Dosyayı da kapatıyor mu?
mrgreenfur

5
Kapatır, ancak ölçeklenebilir değildir, bu yüzden dikkatli olun.
Tin Man

3

Son n satırını dosya_logunuzdan veya .txt dosyasından döndürün

path = File.join(Rails.root, 'your_folder','your_file.log')

last_100_lines = `tail -n 100 #{path}`

1

Daha da etkili bir yol, işletim sisteminin çekirdeğinden bir dosya açmasını ve ardından baytları yavaş yavaş okumasını istemek suretiyle akıştır. Ruby'de satır başına bir dosya okurken, veriler bir seferde 512 bayt dosyadan alınır ve bundan sonra “satırlara” ayrılır.

Dosyanın içeriğini arabelleğe alarak, dosyayı mantıksal yığınlara bölerken G / Ç çağrılarının sayısı azalır.

Misal:

Bu sınıfı uygulamanıza bir hizmet nesnesi olarak ekleyin:

class MyIO
  def initialize(filename)
    fd = IO.sysopen(filename)
    @io = IO.new(fd)
    @buffer = ""
  end

  def each(&block)
    @buffer << @io.sysread(512) until @buffer.include?($/)

    line, @buffer = @buffer.split($/, 2)

    block.call(line)
    each(&block)
  rescue EOFError
    @io.close
 end
end

Onu arayın ve :eachyöntemi bir blok geçirin:

filename = './somewhere/large-file-4gb.txt'
MyIO.new(filename).each{|x| puts x }

Bu ayrıntılı yayında buradan okuyun:

AppSignal Tarafından Ruby Magic Slurping & Streaming Dosyaları


Dikkat edin: bu kod, satır beslemesi ile bitmezse (en azından Linux'ta) son satırı yoksayar.
Jorgen

Bence "block.call (@buffer)" eklemeden önce "@ io.close" eksik eksik satırı alacak. Ancak, Ruby ile sadece bir gün oynadım, bu yüzden yanlış olabilirim. Benim uygulamada çalıştı :)
Jorgen

AppSignal yazısını okuduktan sonra, burada küçük bir yanlış anlaşılma olduğu anlaşılıyor. Arabelleğe alınan bir ES'yi yapan bu gönderiden kopyaladığınız kod, Ruby'nin aslında File.foreach veya IO.foreach (aynı yöntemdir) ile yaptıklarının örnek bir uygulamasıdır. Kullanılmaları gerekir ve bunları bu şekilde yeniden uygulamanız gerekmez.
Peter H. Boling

Ayrıca, çoğu zaman kullanım ve yeniden canlandırma zihniyetini de kullanıyorum. Ama yakut, bir şeyler açmamıza ve içlerinden utanmadan dürtmemize izin veriyor, bu onun avantajlarından biri. Özellikle yakut / raylarda gerçek bir 'zorunluluk' veya 'olmamalı' yoktur. Ne yaptığınızı bildiğiniz ve testler yaptığınız sürece.
Khalil Gharbaoui

0
content = `cat file`

Bu yöntemin en "nadir" yöntem olduğunu düşünüyorum. Belki biraz zordur, ancak catyüklü ise çalışır .


1
Kullanışlı bir hile, ancak kabuğa çağrıda bulunmak gibi birçok tuzak var, 1) komutlar farklı işletim sistemlerinde farklı olabilir, 2) dosya adındaki boşluklardan kaçmanız gerekebilir. Ruby yerleşik işlevlerini kullanmaktan çok daha iyi content = File.read(filename)
Jeff Ward
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.