一行ずつデータを読む簡単なプログラムを書きたい場合には以下のように書ける。
# lineに各行のデータが代入される
for(line <- Source.fromFile("file name").getLines) { ... }
よりファイルを閉じるタイミングや読み書きの性能等を気にするなら以下を参考に。
Loan patternを使う。ファイルの入出力の機能はJavaのライブラリから借りてくる。
def open(fileName:String)(body:InputStream => Unit) : Unit = {
// ディスクへの細かなアクセスを避けるため、バッファを介してファイルを読む
val in = new BufferedInputStream(new FileInputStream(fileName))
try
body(in)
finally
in.close // 開けたら閉じる
}
open("myfile.txt") { f =>
val buf = new Array[Byte](8192) // 8kのバッファを用意
def loop {
val readBytes = f.read(buf) // bufferにデータを読み込む
if(readBytes != -1) { // -1が返るとこれ以上データはない
// use read data here
loop
}
}
loop
}
BufferedReader
を使うと良い。
def open(fileName:String)(body:BufferedReader => Unit) : Unit = {
// ディスクへの細かなアクセスを避けるため、バッファを介してファイルを読む
val in = new BufferedReader(new FileReader(fileName))
try
body(in)
finally
in.close // 開けたら閉じる
}
open("myfile.txt") { f =>
def loop {
val line = f.readLine // 一行ずつ読む
if(line != null) { // nullが返ると読み込み終了
// use read data here
loop
}
}
loop
}
上記のコードではnullの扱いに気をつける必要があり、安全ではない。nullの使用をクラスの内部に閉じ込め、使う側はnullを気にせず一行ずつ操作できるIterator[String]
を提供する形に書き換える。
// 一行ずつ読み込むiteratorを定義
class LineIterator(in:BufferedReader) extends Iterator[String] {
private var nextLine : String = null
def hasNext = {
if(nextLine == null)
nextLine = in.readLine
nextLine != null
}
def next : String = {
if(hasNext) {
val line = nextLine
nextLine = null
line
}
else
Iterator.empty.next
}
}
def open[U](fileName:String)(body:Iterator[String] => U) {
val in = new BufferedReader(new FileReader(fileName))
try
body(new LineIterator(in)) // Iteratorを返す
finally
in.close // 開けたら閉じる
}
open("myfile.txt") { f =>
for(line <- f) { // Iterator#foreachが使える
// 一行ずつ処理する
}
}
Itertorの使用を途中で止めても、loan patternに閉じ込めて実行しているのでファイルがきちんと閉じられる。