scala.io.Sourceとscala.xml.parsing.XhtmlParser
scalaでファイルやURLからテキストを読み込みたいときには、scala.io.Sourceオブジェクトとscala.io.Sourceクラスを利用するのが便利です。
scala.io.Sourceオブジェクトを利用するとファイルからの文字列の読込や、URLからフェッチする処理はこんなふうに簡単に書けます。
import scala.io.Source import scala.xml.XML import scala.xml.parsing.{ConstructingParser,XhtmlParser} // textファイルから読み込んで出力 Source.fromFile("test.txt","UTF-8").foreach(print) // URLからXMLを取得(twitterのxml) val src = scala.io.Source.fromURL("http://twitter.com/statuses/user_timeline/yuroyoro.xml";) val xml = cpa.document() // つぶやきを表示 (xml \\ "statuses" \ "status" \ "text").foreach( t => println( t.text )) // URLのファイルをXHTMLとして読み込んでscala.xml.NodeSeqを作成 val xhtml = XhtmlParser( Source.fromURL("http://www.scala-lang.org/docu/files/api/scala/io/Source.html") ) // titleタグを抽出 xhtml \\ "title"
scala.xml.XMLという便利オブジェクトがあって、これはInputStreamや文字列からxmlを構築してくれます。ただ、Sourceオブジェクトからxmlを構築するメソッドだけなぜかないのです。
Sourceからxmlをparseするにはscala.xml.parsing.ConstructingParserで可能です。
対象がxmlではなくxhtmlならば、scala.xml.parsing.XhtmlParserでparse可能です。ただし、タグの閉じ忘れなどwell-formedなxmlじゃないと当然エラーになりますよ。
scala.io.Sourceはとっても便利ですが、注意しなければならないことがあります。
Streamのcloseは自動的に行われない
これはとっともイタイです。
ちょっとしたスクリプトでファイルを読み込むくらいならいいですけど、ある程度の規模のプログラムでSourceオブジェクトでファイルを読み込みまくると、そのうちjava.nio.BufferOverflowExceptionがthrowされてしまいます。
明示的にcloseしてやれればいいんですが、残念ながらscala.io.Sourceクラスにはcloseメソッドがありません(!?)
ではどうするかというと、こちらのコメント欄にあるように、java.io.InputStreamから Source.fromInputStreamを使って読み込んだあと、InputStreamをcloseするか、SourceをBufferedSourceにキャストしてcloseするしかないです(BufferedSourceにはcloseがあります)。
// InputStreamを使う場合 val in = new FileInputStream("test.txt") try { for(line <- Source.fromInputStream(in).getLines){ println(line) } }finally{ in.close } // キャストする場合 val src = source( path ) try{ XhtmlParser(src) }finally{ src.asInstanceOf[BufferedSource].close }
追記:
scala.io.Sourceのダメさ加減とどうしようって議論がここでされてるらしい。
http://www.nabble.com/feedback-on-scala.io.Source-tc24940271.html