2009-11-05 3 views
12

Ich brauche eine Datei in MB Stücke zu lesen, gibt es einen sauberere Weg, dies in Ruby zu tun:eine Datei in Blöcken in Ruby Read

FILENAME="d:\\tmp\\file.bin" 
MEGABYTE = 1024*1024 
size = File.size(FILENAME) 
open(FILENAME, "rb") do |io| 
    read = 0 
    while read < size 
    left = (size - read) 
    cur = left < MEGABYTE ? left : MEGABYTE 
    data = io.read(cur) 
    read += data.size 
    puts "READ #{cur} bytes" #yield data 
    end 
end 

Antwort

19

aus dem Kochbuch Rubin Angepasst Seite 204:

FILENAME = "d:\\tmp\\file.bin" 
MEGABYTE = 1024 * 1024 

class File 
    def each_chunk(chunk_size = MEGABYTE) 
    yield read(chunk_size) until eof? 
    end 
end 

open(FILENAME, "rb") do |f| 
    f.each_chunk { |chunk| puts chunk } 
end 

Haftungsausschluss: Ich bin ein Ruby-Neuling und habe dies nicht getestet.

+0

Ja, das funktioniert. Ich dachte jedoch, dass IO.read würde werfen, wenn die Anzahl der verbleibenden Bytes weniger als Chunk-Größe war. Ich dachte das, weil ich über IO.readbyte gelesen hatte, das TruncatedDataError werfen wird. Sieht so aus, als ob das NICHT zu lesen gilt. Ein Überbau von meiner Seite. Danke! – teleball

-1
FILENAME="d:/tmp/file.bin" 

class File 
    MEGABYTE = 1024*1024 

    def each_chunk(chunk_size=MEGABYTE) 
    yield self.read(chunk_size) until self.eof? 
    end 
end 

open(FILENAME, "rb") do |f| 
    f.each_chunk {|chunk| puts chunk } 
end 

Es funktioniert, mbarkhau. Ich habe gerade die konstante Definition in die Dateiklasse verschoben und der Klarheit halber ein paar "Selbst" hinzugefügt.

+2

Ich würde nicht die zusätzliche Konstante MEGABYTE stattdessen verwenden: 'def each_chunk (chunk_size = 2 ** 20)' – asaaki

7

Alternativ, wenn Sie nicht wollen, monkeypatch File:

until my_file.eof? 
    do_something_with(my_file.read(bytes)) 
end 

Zum Beispiel Streaming ein hochgeladenes tempfile in eine neue Datei:

# tempfile is a File instance 
File.open(new_file, 'wb') do |f| 
    # Read in small 65k chunks to limit memory usage 
    f.write(tempfile.read(2**16)) until tempfile.eof? 
end 
0

Wenn Sie das Ruby-docs check out: http://ruby-doc.org/core-2.2.2/IO.html gibt es eine Zeile, die so geht:

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

Der einzige Vorbehalt ist. Da kann dieser Prozess die temporäre Datei schneller als der erzeugten Strom lesen, IMO sollte eine Latenz in geworfen werden.

IO.foreach("/tmp/streamfile") {|line| 
    ParseLine.parse(line) 
    sleep 0.3 #pause as this process will discontine if it doesn't allow some buffering 
} 
1

Sie IO#each(sep, limit) verwenden können, und setzen sep-nil oder eine leere Zeichenfolge, zum Beispiel :

chunk_size = 1024 
File.open('/path/to/file.txt').each(nil, chunk_size) do |chunk| 
    puts chunk 
end 
+0

Das ist einfach falsch. –

+1

@EricDuminil Vielen Dank für die Erinnerung, ich habe das Argument sep vergessen. Es sollte jetzt eine Datei in Blöcken lesen. –