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


312

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()

12
適切なツールを知っていれば、Javaはそれほど悪くありません。org.apache.commons.io.FileUtilsをインポートします。FileUtils.readFileToString(new File( "file.txt"、 "UTF-8")
smartnut007

25
このコメントは言語設計の要点を逃しています。したがって、実行したい操作に正確に対応する単純なライブラリ関数を使用できる言語は、関数呼び出し構文と同じくらい優れています。無限で100%記憶されたライブラリを考えると、すべてのプログラムは単一の関数呼び出しで実装されます。プログラミング言語は、特定の結果を達成するために既存のプレハブコンポーネントが少なくて済む場合に適しています。
Chris Mountford、2014

回答:


429
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"

48
パーティーには遅すぎますが、トランクで "io.File(" / etc / passwd ")。slurp"を実行できることを知らない人は嫌いです。
psp

28
@extempore本当に感謝していると思ったら、本当にごめんなさい。Scala言語のサポート、および私が提起した問題を個人的に調査したり、私が抱えている問題の解決策を提案したり、何かを説明したりするたびに、私は深く感謝します。それでは、機会を利用してscala.ioをまともで価値のあるものに変えてくれてありがとうございます。これからはもっと声に出していきますが、まだ名前が嫌いです。
ダニエルC.ソブラル

49
「slurp」は、長年Perlでファイル全体を一度に読み取るための名前です。Perlは、Cファミリの言語よりも内臓的で非公式な命名の伝統を持っています。これは、不快に思う人もいるかもしれませんが、この場合は当てはまると思います。slurp()を実行すると、タイプするだけでいたずらをしていることがわかります。
マーカスダウニング

15
File.read()の方がわかりやすい名前で、RubyとPython以外にも一貫しています。
ブレンダンOConnor 2009

26
@extempore:人々がうんざりするのを止めることはできません。それはありのままです。一部の人々はあなたが行ったすべての選択が好きではないことを気にしないでください。それはただの人生です、あなたは誰もが満足することはできません:)
Alex Baranosky 09/09/25

58

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

import scala.io.Source._

これで、次のことができるようになります。

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

ファイル全体を1つのファイルに読み込むのには注意が必要ですString。これは非常に悪い習慣であり、あなたが思っているよりも早く、より難しくあなたに噛み付くでしょう。このgetLinesメソッドは、typeの値を返しますIterator[String]。これは事実上、ファイルへの遅延カーソルであり、メモリの過剰を危険にさらすことなく、必要なデータだけを調べることができます。

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


OK。出典に対する否定的な印象についての話があります。私はかつて今とは異なる状況にあり、メモリに収まらない非常に大きなファイルを持っていました。ソースを使用すると、プログラムがクラッシュしました。一度に全部を読み込もうとしていることがわかりました。
ブレンダンOConnor 09

7
ソースはファイル全体をメモリに読み込むことを想定していません。getLinesまたはコレクションを生成するその他のメソッドの後にtoListを使用すると、すべてがメモリに格納されます。現在、Sourceはハックであり、注意深く考え抜かれたライブラリではなく、仕事を成し遂げることを目的としています。Scala 2.8では改善される予定ですが、優れたI / O APIを定義するためにScalaコミュニティが積極的に活動する機会は確実にあります。
ダニエルC.ソブラル

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

6
元の回答に「getLines」を追加すると、すべての改行が削除されます。"Source.fromFile(" file.txt "、" utf-8 ")。mkString"である必要があります。
Joe23、

9
ダニエルCの私のコメントも参照してください。ソブラルの回答-この使用はSourceインスタンスを閉じないため、Scalaはファイルのロックを保持する場合があります。
djb 2011

26

(編集:これはScala 2.9では機能せず、おそらく2.8でも機能しません)

トランクを使用:

scala> io.File("/etc/passwd").slurp
res0: String = 
##
# User Database
# 
... etc

14
slurp」?私たちは本当に明白で直感的な名前を捨てましたか?問題slurpは、少なくとも第一言語として英語を使う人にとっては、実際にはそれが理にかなっているかもしれないということですが、それを最初から考えることは決してないでしょう!
ダニエルC.ソブラル

5
この質問/回答につまずいただけです。File2.8.0にはもうないのでは?
huynhjl 2010

4
slurpは素晴らしい音です。:)私はそれを期待しませんでしたが、画面への出力が「印刷」という名前になることも期待していませんでした。slurp素晴らしい!:)素晴らしかったですか?見つかりません。;(
ユーザーが不明

5
scala-2.10.0では、パッケージ名はscala.reflect.io.Fileです。この「ファイル」についての質問です。即座に、このファイルが「実験的」とマークされているのはなぜですか?安全ですか?ファイルシステムへのロックを解放しますか?
VasiliNovikov 2013年

4
slurpには、perlから始まったこの目的のための長い歴史があります
Chris Mountford '27 / 11/15

18
import java.nio.charset.StandardCharsets._
import java.nio.file.{Files, Paths}

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

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


7

Source.fromFileに問題があると言われました。個人的には、Source.fromFileで大きなファイルを開くときに問題が発生し、Java InputStreamsを使用する必要がありました。

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


6

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

もう1つ: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")

5

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

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

また、ここでの多くの答えはCharsetを忘れます。常に明示的に提供することをお勧めします。そうしないと、1日でヒットします。


4

ファイルのオープンと読み取りの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

3

数人がscala.io.Sourceを言及したように、接続リークのために回避するのが最善です。

おそらくscalaxとcommons-ioのような純粋なjava libsは、新しいインキュベータープロジェクト(つまり、scala-io)がマージされるまでの最良のオプションです。


3

scala ioのPathを使用してファイルを読み取り、処理することもできます。

import scalax.file.Path

これを使用して、ファイルパスを取得できます。

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

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


3

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

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

Source.scalaに注意してください。詳細については、Scalaの高速テキストファイルの読み取りとメモリへのアップロードを参照してください。


3

すべての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
}

ストリームを閉じる必要があります-エラーが発生した場合val content = source.mkString
Andrzej Jozwik 2018

の+1 Codecsbt testIntellijのテストコマンドがすべてのテストに合格する一方で、設定できないためにテストが失敗しました。そして、あなたは使用することができますdef usingから、この
ミハイルIonkin

3

サードパーティの依存関係を気にしない場合は、私のOS-Libライブラリの使用を検討してください。これにより、ファイルの読み取り/書き込みとファイルシステムの操作が非常に便利になります。

// 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")

一行のためのヘルパーとのバイトを読み込むチャンクを読み出し行を読み取り、および他の多くの有用な/一般的な操作


2

明らかな疑問は、「なぜファイル全体を読みたいのか」ということです。ファイルが非常に大きくなる場合、これは明らかにスケーラブルなソリューションではありません。scala.io.Sourceあなたが戻っていますIterator[String]から、getLines非常に便利かつ簡潔である方法。

これは、変換するために、基盤となるJavaのIOユーティリティを使用して暗黙の型変換を思い付くために仕事のあまりないFileReaderまたはInputStreamString。スケーラビリティの欠如は、これを標準APIに追加しないことが正しいことを意味すると思います。


12
マジ?実際にメモリに収まるのに問題がある実際にいくつのファイルを定期的に読みますか?私がこれまで扱ってきたプログラムの大部分のファイルの大部分は、メモリに収まるほど簡単に小さくなります。率直に言って、ビッグデータファイルは例外であり、それらを読み書きする場合は、それに気づき、それに応じてプログラムする必要があります。
クリストファー

8
oxbow_lakes、私は同意しません。サイズが将来大きくならない小さなファイルに関連する多くの状況があります。
ブレンダンOConnor 09

4
私はそれらが例外であることには同意します-しかし、メモリ全体の読み取りメモリがJDKにもScala SDKにもない理由はそこにあると思います。これは、自分で書くための3行のユーティリティメソッドです。それを
乗り越える

1

すべての行を出力します。たとえば、Java BufferedReaderを使用して、すべての行を読み取り、出力します。

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

同等:

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

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

引数でファイルパスを指定すると、すべての行が返されます


3
これは他の答えが提供していないことを何が提供していますか?
jwvh 2017

他の回答を見たことがありません...私はここに投稿できると思ったので投稿しました...うまくいけば誰にも害を及ぼさないでしょう:)
Apurw

1
あなたは本当にそれらを読むべきです。ほとんどは非常に有益です。8歳の人でも関連情報があります。
jwvh 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.