web-dev-qa-db-ja.com

Scalaでファイル全体を読みますか?

Scalaでファイル全体をメモリに読み込むための単純で標準的な方法は何ですか? (理想的には、文字エンコーディングを制御します。)

私が思い付くことができる最高のものは:

scala.io.Source.fromPath("file.txt").getLines.reduceLeft(_+_)

あるいは、私は Javaのひどい意味のイディオム のうちの1つを使用することになっています。

import Java.util.Scanner
import Java.io.File
new Scanner(new File("file.txt")).useDelimiter("\\Z").next()

メーリングリストでの議論を読んでいることから、scala.io.Sourceが正規のI/Oライブラリにさえなっていることさえ私にははっきりしません。その意図する目的が正確にはわからない。

...シンプルで覚えやすいものをお願いします。たとえば、これらの言語では、慣用句を忘れるのは非常に困難です。

Ruby    open("file.txt").read
Ruby    File.read("file.txt")
Python  open("file.txt").read()
299
Brendan OConnor
val lines = scala.io.Source.fromFile("file.txt").mkString

ところで、 "scala."はとにかくスコープ内にあるので本当に必要ではありません、そしてもちろん、ioの内容を完全にまたは部分的にインポートして、 "io"の前に置くことを避けられます。も。

しかしながら、上記はファイルを開いたままにします。問題を避けるためには、次のようにしてそれを閉じるべきです。

val source = scala.io.Source.fromFile("file.txt")
val lines = try source.mkString finally source.close()

上記のコードに関するもう1つの問題は、その実装の性質上、非常に遅いということです。大きなファイルの場合は、次のものを使用してください。

source.getLines mkString "\n"
413

Danielのソリューションを拡張するためだけに、ファイル操作を必要とするファイルに次のインポートを挿入することで物事を大幅に短縮することができます。

import scala.io.Source._

これで、あなたは今することができます:

val lines = fromFile("file.txt").getLines

ファイル全体を1つのStringに読み込むことには用心深いでしょう。それは非常に悪い習慣です。 getLinesメソッドは、タイプIterator[String]の値を返します。これは事実上ファイルへの怠惰なカーソルなので、メモリ不足を招くことなく必要なデータだけを調べることができます。

ああ、そしてSourceに関するあなたの暗黙の質問に答えるために:はい、それは標準的なI/Oライブラリです。ほとんどのコードは、その下位レベルのインターフェースと既存のフレームワークとのより良い互換性のためにJava.ioを使用しますが、特に単純なファイル操作の場合は、Sourceを使用する必要があります。

56
Daniel Spiewak
// for file with utf-8 encoding
val lines = scala.io.Source.fromFile("file.txt", "utf-8").getLines.mkString
36
Walter Chang

(編集:これはscala 2.9ではうまくいかないし、おそらく2.8でもうまくいかない)

トランクを使用:

scala> io.File("/etc/passwd").Slurp
res0: String = 
##
# User Database
# 
... etc
26
psp
import Java.nio.charset.StandardCharsets._
import Java.nio.file.{Files, Paths}

new String(Files.readAllBytes(Paths.get("file.txt")), UTF_8)

文字エンコードを制御します。クリーンアップするリソースはありません。また、おそらく最適化されています(例:Files.readAllBytesはファイルのサイズに適したバイト配列を割り当てる)。

17
Paul Draper

Source.fromFileには問題があると言われました。個人的には、Source.fromFileで大きなファイルを開くのに問題があり、Java InputStreamに頼らなければなりませんでした。

もう一つの興味深い解決策はscalaxを使うことです。これはscalaxヘルパーでファイルを開くためにManagedResourceを使ってログファイルを開くいくつかのよくコメントされたコードの例です: http://pastie.org/pastes/420714

7
Ikai Lan

Scala.io.SourceでgetLines()を使用すると、行末文字に使用された文字(\ n、\ r、\ r\nなど)は破棄されます。

次は文字ごとに保存し、過度の文字列連結をしないようにします(パフォーマンスの問題)。

def fileToString(file: File, encoding: String) = {
  val inStream = new FileInputStream(file)
  val outStream = new ByteArrayOutputStream
  try {
    var reading = true
    while ( reading ) {
      inStream.read() match {
        case -1 => reading = false
        case c => outStream.write(c)
      }
    }
    outStream.flush()
  }
  finally {
    inStream.close()
  }
  new String(outStream.toByteArray(), encoding)
}
6
Muyyatin

Javaと同じように、CommonsIOライブラリを使用します。

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

また、ここでの多くの答えはCharsetを忘れています。それを常に明示的に提供することがより良いです、さもなければそれはいつの日かヒットします。

5
Dzmitry Lazerka

ファイルを開いたり読み込んだりするためのRuby構文をエミュレートする(そして意味を伝える)ために、この暗黙のクラス(Scala 2.10以降)を考えてください。

import Java.io.File

def open(filename: String) = new File(filename)

implicit class RichFile(val file: File) extends AnyVal {
  def read = io.Source.fromFile(file).getLines.mkString("\n")
}

この方法では、

open("file.txt").read
4
elm

もう一つ: https://github.com/pathikrit/better-files#streams-and-codecs

内容をメモリにロードせずにファイルを丸めるためのさまざまな方法:

val bytes  : Iterator[Byte]            = file.bytes
val chars  : Iterator[Char]            = file.chars
val lines  : Iterator[String]          = file.lines
val source : scala.io.BufferedSource   = file.content 

読み書きが可能なものには、独自のコーデックを指定することもできます(指定しない場合はscala.io.Codec.defaultと見なされます)。

val content: String = file.contentAsString  // default codec
// custom codec:
import scala.io.Codec
file.contentAsString(Codec.ISO8859)
//or
import scala.io.Codec.string2codec
file.write("hello world")(codec = "US-ASCII")
4
pathikrit

scala ioからのパスを使ってファイルを読み込んで処理することもできます。

import scalax.file.Path

今、あなたはこれを使ってファイルパスを取得することができます: -

val filePath = Path("path_of_file_to_b_read", '/')
val lines = file.lines(includeTerminator = true)

ターミネータを含めることもできますが、デフォルトではfalseに設定されています。

3
Atiq

少数の人が述べたように scala.io.Source は接続リークのため避けるべきです。

新しいインキュベータプロジェクト(すなわちscala-io)がマージされるまでは、おそらくscalaxとcommons-ioのような純粋なJavaライブラリが最良の選択肢です。

3
poko

(大きな)ファイルの全体的な読み取り/アップロードを高速化するには、次のようにbufferSizeSource.DefaultBufSize2048に設定)のサイズを増やすことを検討してください。

val file = new Java.io.File("myFilename")
io.Source.fromFile(file, bufferSize = Source.DefaultBufSize * 2)

Source.scala 。さらなる議論については、 Scalaの高速テキストファイルを読み、メモリにアップロードする を参照してください。

3
elm

明白な問題は、"なぜファイル全体を読み込むのですか?"ファイルが非常に大きくなった場合、これは明らかにスケーラブルな解決策ではありません。 scala.io.SourcegetLinesメソッドからIterator[String]を返すので、非常に便利で簡潔です。

基礎となるJava IOユーティリティを使用してFileReader、またはInputStreamStringに変換する暗黙的な変換を行うのは、あまり面倒な作業ではありません。私はスケーラビリティの欠如が標準のAPIにこれを追加しないことが彼らが正しいということを意味すると思います。

2
oxbow_lakes
import scala.io.source
object ReadLine{
def main(args:Array[String]){
if (args.length>0){
for (line <- Source.fromLine(args(0)).getLine())
println(line)
}
}

引数にはファイルパスを指定でき、すべての行を返します。

1
Apurw

サードパーティの依存関係を気にしないのであれば、my OS-Lib library の使用を検討してください。これにより、ファイルの読み書きやファイルシステムの操作が非常に便利になります。

// Make sure working directory exists and is empty
val wd = os.pwd/"out"/"splash"
os.remove.all(wd)
os.makeDir.all(wd)

// Read/write files
os.write(wd/"file.txt", "hello")
os.read(wd/"file.txt") ==> "hello"

// Perform filesystem operations
os.copy(wd/"file.txt", wd/"copied.txt")
os.list(wd) ==> Seq(wd/"copied.txt", wd/"file.txt")

バイトの読み込みチャンクの読み込み行の読み込み 、およびその他多くの便利な/一般的な操作のための1行ヘルパー

1
Li Haoyi

java BufferedReaderを使用してすべての行を読み取り、それを印刷します。

scala.io.Source.fromFile("test.txt" ).foreach{  print  }

同等のもの:

scala.io.Source.fromFile("test.txt" ).foreach( x => print(x))
1
gordonpro

1行ごとに解析してから再び連結する必要はありません。

Source.fromFile(path)(Codec.UTF8).mkString

これを使うのが好きです。

import scala.io.{BufferedSource, Codec, Source}
import scala.util.Try

def readFileUtf8(path: String): Try[String] = Try {
  val source: BufferedSource = Source.fromFile(path)(Codec.UTF8)
  val content = source.mkString
  source.close()
  content
}
1
comonad