これは機能します:
f = File.new("myfile").readlines
f[0] #=> "line 1"
f[21] #=> "line 22"
しかし、非常に大きなファイルがあり、数行しか読み取る必要がない場合はどうすればよいでしょうか。ファイルを配列にロードせずに、特定の行を探してRubyで読み取ることは可能ですか?
(stdin の場合のように) ストリームをランダムにシークすることはできません。確かに、ファイル全体をロードせずにこれを行う方法が必要です。
これは機能します:
f = File.new("myfile").readlines
f[0] #=> "line 1"
f[21] #=> "line 22"
しかし、非常に大きなファイルがあり、数行しか読み取る必要がない場合はどうすればよいでしょうか。ファイルを配列にロードせずに、特定の行を探してRubyで読み取ることは可能ですか?
(stdin の場合のように) ストリームをランダムにシークすることはできません。確かに、ファイル全体をロードせずにこれを行う方法が必要です。
目的のために、each_line
イテレータを使用with_index
して、現在の行の行番号 (0 から数えます) を取得できます。
File.open('myfile') do |file|
file.each_line.with_index do |line, lineno|
case lineno
when 0
# line 1
when 21
# line 22
end
end
end
の代わりに を使用しopen
てブロックを渡すことによりnew
、ブロック実行の最後にファイルが適切に閉じられることが保証されます。
更新このwith_index
メソッドは、使用する開始インデックスを指定するオプションの引数を受け入れるため、上記のコードは次のように記述したほうがよいでしょう。
file.each_line.with_index(1) do |line, lineno|
case lineno
when 1
# line 1
end
end
Jack と toro2k の回答 (ほぼ同じ回答) を使用しましたが、自分のユースケースに合わせて変更しました。私が望むかもしれない場所:ファイルを開き、順序が常に連続しているとは限らない複数のランダムな行を探します。これは私が思いついたものです(要約):
class LazyFile
def initialize(file)
@content = File.new(file)
end
def [](lineno)
@content.rewind if @content.lineno > lineno
skip = lineno - @content.lineno
skip.times { @content.readline }
@content.readline
end
end
file = LazyFile("myfile")
file[1001]