Scalaでファイルに書き込む方法は?


157

読むために、便利な抽象化がありSourceます。テキストファイルに行を書き込むにはどうすればよいですか?


1
Javaでこれを行う方法を知っている場合は、Scalaでも同じ方法を使用できます。あなたの質問は、特にScala標準ライブラリに関するものですか?
ウィーティー

1
@wheatiesはい、これをscalaで行う最良の方法
yura

このライブラリは本当に良いです:github.com/pathikrit/better-files
Robin

LihaoyiのOS-Libライブラリgithub.com/lihaoyi/os-lib
WeiChing林煒清

回答:


71

2019年(8年後)を編集、Scala-IOはあまりアクティブではない、もしあれば、Li Haoyiは彼自身のライブラリを提案しlihaoyi/os-lib、彼は以下提示する

2019年6月、Xavier Guihot彼の回答でUsing自動リソース管理を実行するためのユーティリティであるライブラリについて言及しています


編集(2011年9月):Eduardo CostaがScala2.9について質問して以来、Rick-777が 2009年半ば以降scalax.IOのコミット履歴はほとんど存在しないとコメントしています...

Scala-IOの場所が変更されました。JesseEicharから GitHubリポジトリを参照しください( SOでも):

Scala IOアンブレラプロジェクトは、IOのさまざまな側面と拡張のためのいくつかのサブプロジェクトで構成されています。
Scala IOには2つの主要なコンポーネントがあります。

  • コア -コアは主に、任意のソースおよびシンクとの間でデータを読み書きすることを扱います。コーナーストーンの特徴はInputOutputありSeekable、コアAPIを提供します。
    他の重要なクラスはResourceReadCharsかつWriteCharsです。
  • ファイル -ファイルは、Java 7 NIOファイルシステムとSBT PathFinder APIの組み合わせに基づくFile(と呼ばれるPath)APIです。
    PathまたFileSystem、Scala IOファイルAPIへの主要なエントリポイントです。
import scalax.io._

val output:Output = Resource.fromFile("someFile")

// Note: each write will open a new connection to file and 
//       each write is executed at the begining of the file,
//       so in this case the last write will be the contents of the file.
// See Seekable for append and patching files
// Also See openOutput for performing several writes with a single connection

output.writeIntsAsBytes(1,2,3)
output.write("hello")(Codec.UTF8)
output.writeStrings(List("hello","world")," ")(Codec.UTF8)

元の回答(2011年1月)、古いscala-ioの場所:

Scala2.9を待ちたくない場合は、scala-incubator / scala-ioライブラリを使用できます。
(「Scala Sourceが基になるInputStreamを閉じないのはなぜですか?」で述べたように)

サンプルを見る

{ // several examples of writing data
    import scalax.io.{
      FileOps, Path, Codec, OpenOption}
    // the codec must be defined either as a parameter of ops methods or as an implicit
    implicit val codec = scalax.io.Codec.UTF8


    val file: FileOps = Path ("file")

    // write bytes
    // By default the file write will replace
    // an existing file with the new data
    file.write (Array (1,2,3) map ( _.toByte))

    // another option for write is openOptions which allows the caller
    // to specify in detail how the write should take place
    // the openOptions parameter takes a collections of OpenOptions objects
    // which are filesystem specific in general but the standard options
    // are defined in the OpenOption object
    // in addition to the definition common collections are also defined
    // WriteAppend for example is a List(Create, Append, Write)
    file.write (List (1,2,3) map (_.toByte))

    // write a string to the file
    file.write("Hello my dear file")

    // with all options (these are the default options explicitely declared)
    file.write("Hello my dear file")(codec = Codec.UTF8)

    // Convert several strings to the file
    // same options apply as for write
    file.writeStrings( "It costs" :: "one" :: "dollar" :: Nil)

    // Now all options
    file.writeStrings("It costs" :: "one" :: "dollar" :: Nil,
                    separator="||\n||")(codec = Codec.UTF8)
  }

15
Scala 2.9バージョンについてはどうですか?:)
エドゥアルドコスタ

scalaxプロジェクトは死んでいるようです(2009年6月以降、コミットはありません)。これは正解?scalaxコミット履歴
Rick-777

@Eduardo:私はscala-ioライブラリの新しい場所(Scala2.9で更新されたgithub.com/jesseeichar/scala-io/issues/20)で私の回答を完成させました
VonC '28

10
これは本当にScala 2.10の現在の提案ですか?Scala IOを使用しますか?コアScalaにはまだ何もありませんか?
Phil

2
私はscalax.ioを使用したことがありませんが、これらのサンプル行から判断すると、そのAPI設計はかなり悪いようです。1つのインターフェイスで文字データとバイナリデータのメソッドを混在させることはあまり意味がなく、見つけるのが難しいエンコードのバグにつながる可能性が非常に高くなります。java.io(Reader / Writer対InputStream / OutputStream)の設計ははるかに優れているようです。
jcsahnwaldt Reinstate Monica 2013

211

これは、標準のScalaに欠けている機能の1つであり、私が個人用ライブラリに追加するのに非常に便利であることがわかりました。(おそらく、個人用ライブラリも必要です。)コードは次のようになります。

def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) {
  val p = new java.io.PrintWriter(f)
  try { op(p) } finally { p.close() }
}

そしてそれはこのように使用されます:

import java.io._
val data = Array("Five","strings","in","a","file!")
printToFile(new File("example.txt")) { p =>
  data.foreach(p.println)
}

1
新しいjava.io.PrintWriter()は、プラットフォームのデフォルトのエンコーディングを使用します。これは、結果ファイルの移植性があまり高くないことを意味します。たとえば、後で電子メールで送信できるファイルを作成する場合は、エンコードを指定できるPrintWriterコンストラクターを使用する必要があります。
jcsahnwaldt Reinstate Monica 2013

@JonaChristopherSahnwaldt-もちろん、特殊なケースではエンコーディングを指定する必要があります。プラットフォームのデフォルトは、平均して最も賢明なデフォルトです。と同じSource(デフォルトはデフォルトのエンコーディング)。もちろん、これが一般的なニーズであるとenc: Option[String] = Noneわかったf場合は、たとえばパラメータを後に追加できます。
レックスカー

6
@RexKerr-同意しません。ほとんどすべての場合、エンコーディングを指定する必要があります。私が遭遇するほとんどのエンコーディングエラーは、人々がエンコーディングについて理解していない、または考えていないために発生します。APIが多すぎると、デフォルトを使用できなくなるため、デフォルトを使用し、それさえ知らない。現在、最も適切なデフォルトはおそらくUTF-8です。多分あなたは、ASCIIで書くことができる英語と他の言語でのみ働くでしょう。あなたはラッキーです。私はドイツに住んでいて、覚えているよりも多くの壊れたウムラウトを修正する必要がありました。
jcsahnwaldt Reinstate Monica 2013

3
@JonaChristopherSahnwaldt-これは、誰もが常にそれを指定するように強制するのではなく、適切なデフォルトのエンコーディングを使用する理由です。しかし、Macを使用していて、Javaで作成されたファイルがMac OS Romanエンコードされていないためにgobbledygookである場合、害を及ぼすよりも効果があるかどうかはわかりません。彼らが文字セットについて合意していないのは、プラットフォームのせいだと思います。個人の開発者として、文字列を入力しても問題は解決されません。(UTF-8に同意するすべての開発者が同意しますが、デフォルトとしてそのまま使用できます。)
Rex Kerr

@JonaChristopherSahnwaldt +10壊れたウムラウトをすべて修正してください。ハンマー、たぶんホールパンチは使えませんか?または、それらはすでに穴を埋める必要がある穴ですか、おそらくこの男はyoutube.com/watch?v=E-eBBzWEpwEを助けることができます。 8
ダボス2017

50

レックスカーの回答に似ていますが、より一般的です。まず、ヘルパー関数を使用します。

/**
 * Used for reading/writing to database, files, etc.
 * Code From the book "Beginning Scala"
 * http://www.amazon.com/Beginning-Scala-David-Pollak/dp/1430219890
 */
def using[A <: {def close(): Unit}, B](param: A)(f: A => B): B =
try { f(param) } finally { param.close() }

次に、これを次のように使用します。

def writeToFile(fileName:String, data:String) = 
  using (new FileWriter(fileName)) {
    fileWriter => fileWriter.write(data)
  }

そして

def appendToFile(fileName:String, textData:String) =
  using (new FileWriter(fileName, true)){ 
    fileWriter => using (new PrintWriter(fileWriter)) {
      printWriter => printWriter.println(textData)
    }
  }


39
誤解しないでください。私はあなたのコードが好きで、それは非常に教育的ですが、単純な問題のためにそのような構成要素を見るほど、それは古い "hello world"ジョークを思い出させます:ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-)(私からの投票1)。
greenoldman '10

4
ワンライナーを書いている場合は、何も問題ありません。重要なプログラムを作成している場合(メンテナンスと進化の継続的な必要がある大規模なプログラム)、この種の考え方は、ソフトウェアの品質が最も急速で悪質な低下につながります。
Randall Schulz

3
ある程度の練習をするまで誰もが「スカラの目」を持つわけではありません-このコード例が「Beginning」Scalaから来ているのを見るのはおかしい
asyncwait

asyncwait "beginning" scala ...これまでで最も皮肉なタイトル、注:私はこの本を読んでいます...そして今、私はそれを理解し始めています。 ........
user1050817 '19 / 07/19

1
ここでの問題はScalaのトリックではなく、冗長性とスタイルの悪さです。これをもっと読みやすいように編集しました。私のリファクタリングの後、それはたったの4行です(まあ、4行はIDE行の長さで、画面に合わせるためにここでは6行を使用しています)。私見それは今とても良い答えです。
samthebest 2013年

38

簡単な答え:

import java.io.File
import java.io.PrintWriter

def writeToFile(p: String, s: String): Unit = {
    val pw = new PrintWriter(new File(p))
    try pw.write(s) finally pw.close()
  }

1
@samthebestライブラリを追加できますimportか?
ダニエル

1
Java 7以降では、代わりにjava.nio.fileを使用してください:def writeToFile(file:String、stringToWrite:String):Unit = {val writer = Files.newBufferedWriter(Paths.get(file))try writer.write(stringToWrite)finally writer.close()}
Eシンドラー

20

他の回答の編集は拒否されたため、別の回答を与えます。

これは最も簡潔でシンプルな答えです(Garret Hallと同様)

File("filename").writeAll("hello world")

これはJus12に似ていますが、冗長性がなく、正しいコードスタイルがあります。

def using[A <: {def close(): Unit}, B](resource: A)(f: A => B): B =
  try f(resource) finally resource.close()

def writeToFile(path: String, data: String): Unit = 
  using(new FileWriter(path))(_.write(data))

def appendToFile(path: String, data: String): Unit =
  using(new PrintWriter(new FileWriter(path, true)))(_.println(data))

中括弧try finallyやラムダは不要であり、プレースホルダー構文の使用法に注意してください。また、より良い命名に注意してください。


2
申し訳ありませんが、コードは想像できますが、implemented前提条件を満たしていません。実装されていないコードは使用できません。それはデフォルトでは利用できず、よく知られていませんので、それを見つける方法を伝える必要があることを意味します。
Val

15

以下は、Scalaコンパイラライブラリを使用した簡潔なワンライナーです。

scala.tools.nsc.io.File("filename").writeAll("hello world")

または、Javaライブラリを使用する場合は、このハックを実行できます。

Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}

何が輸入されますか?つまり、ファイルはどこから来るのですか?
ベンハッチソン

Scalaコンパイラー・ライブラリー。
Garrett Hall

3
実行可能ではなくなりました(Scala 2.11にはありません)
ブレントファウスト

1
あなたは何について話していますか?scala.tools.nsc.io.File("/tmp/myFile.txt")Scala 2.11.8で動作します。

1
現在scala.reflect.io.Fileにあります
Keith Nordstrom

13

Stringを使用したへの保存/読み取り用の1つのライナーjava.nio

import java.nio.file.{Paths, Files, StandardOpenOption}
import java.nio.charset.{StandardCharsets}
import scala.collection.JavaConverters._

def write(filePath:String, contents:String) = {
  Files.write(Paths.get(filePath), contents.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE)
}

def read(filePath:String):String = {
  Files.readAllLines(Paths.get(filePath), StandardCharsets.UTF_8).asScala.mkString
}

これは大きなファイルには適していませんが、機能します。

いくつかのリンク:

java.nio.file.Files.write
java.lang.String.getBytes
scala.collection.JavaConverters
scala.collection.immutable.List.mkString


なぜこれが大きなファイルに適していないのですか?
Chetan Bhasin

2
@ChetanBhasinおそらく、ファイルにストリーミングするのではなく、新しいバイト配列にwriteコピーcontentsするため、ピーク時には、contents単独の2倍のメモリを使用します。
Daniel Werner、

10

残念ながら、トップの回答では、Scala-IOは死んでいます。サードパーティの依存関係を使用しても構わない場合は、私の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")

それはのためのワンライナー持つファイルへの書き込みファイルに追加するファイルを上書きし、そして他の多くの有益な/一般的な操作を


このアップデートをありがとう。賛成。わかりやすくするために、上で自分の回答を参照しました。
VonC、

7

私が書いたマイクロライブラリ:https : //github.com/pathikrit/better-files

file.appendLine("Hello", "World")

または

file << "Hello" << "\n" << "World"

ここでも同様-この質問は、scalaを使用してファイルを書き込む方法をグーグルで検索する際のトップヒットの1つです-プロジェクトが大きくなったので、答えを少し広げたいと思うかもしれません。
asac

6

以降Scala 2.13、標準ライブラリは専用のリソース管理ユーティリティを提供します。Using

この場合、PrintWriterまたはなどのリソースで使用でき、ファイルに書き込むためにBufferedWriter拡張さAutoCloseableれます。何があっても、後でリソースを閉じます。

  • たとえば、java.ioapi:

    import scala.util.Using, java.io.{PrintWriter, File}
    
    // val lines = List("hello", "world")
    Using(new PrintWriter(new File("file.txt"))) {
      writer => lines.foreach(writer.println)
    }
  • またはjava.nioapiを使用:

    import scala.util.Using, java.nio.file.{Files, Paths}, java.nio.charset.Charset
    
    // val lines = List("hello", "world")
    Using(Files.newBufferedWriter(Paths.get("file.txt"), Charset.forName("UTF-8"))) {
      writer => lines.foreach(line => writer.write(line + "\n"))
    }

6

2019年9月1日の更新:

  • Scala 2.13から、使用を好む scala.util.Using勧めします
  • コードがスローした場合にスローされたfinallyオリジナルを飲み込むバグを修正ExceptiontryfinallyException

Scalaでファイルを簡単に書き込む方法に関するこれらすべての回答を確認したところ、そのうちのいくつかは非常に優れていて、3つの問題がありました。

  1. Jus12の答え、使用してヘルパーメソッドのためにカリー化の使用はスカラ/ FP初心者のための非自明です
  2. 低レベルのエラーをカプセル化する必要がある scala.util.Try
  3. Scala / FPを初めて使用するJava開発者に、依存リソースを正しく入れ子にしてclose、各依存リソースに対して逆の順序でメソッドを実行する方法を示す必要があります。注:特に障害が発生した場合に依存リソースを逆の順序で閉じることは、のjava.lang.AutoCloseable非常に悪質でバグの発見や実行時の障害につながる傾向が仕様

始める前に、私の目標は簡潔さではありません。Scala / FPの初心者、通常はJavaの初心者の理解を容易にするためです。最後に、すべてのビットをまとめて簡潔にします。

まず、usingメソッドを使用するために更新する必要がありますTry(ここでも、簡潔さは目標ではありません)。名前は次のように変更されtryUsingAutoCloseableます。

def tryUsingAutoCloseable[A <: AutoCloseable, R]
  (instantiateAutoCloseable: () => A) //parameter list 1
  (transfer: A => scala.util.Try[R])  //parameter list 2
: scala.util.Try[R] =
  Try(instantiateAutoCloseable())
    .flatMap(
      autoCloseable => {
        var optionExceptionTry: Option[Exception] = None
        try
          transfer(autoCloseable)
        catch {
          case exceptionTry: Exception =>
            optionExceptionTry = Some(exceptionTry)
            throw exceptionTry
        }
        finally
          try
            autoCloseable.close()
          catch {
            case exceptionFinally: Exception =>
              optionExceptionTry match {
                case Some(exceptionTry) =>
                  exceptionTry.addSuppressed(exceptionFinally)
                case None =>
                  throw exceptionFinally
              }
          }
      }
    )

上記のtryUsingAutoCloseableメソッドの最初は、通常の単一のパラメーターリストではなく2つのパラメーターリストがあるように見えるため、混乱を招く可能性があります。これをカレーといいます。そして、カレーがどのように機能するか、またはそれが時折どこにあるかについては詳しくは触れません。役立つ。この特定の問題空間では、それが仕事に適したツールであることがわかります。

次に、をtryPrintToFile作成(または既存のものを上書き)しFile、を書き込むメソッドを作成する必要がありますList[String]。これは、FileWriterによってカプセル化されたBufferedWriterを使用し、次にによってカプセル化されますPrintWriter。また、パフォーマンスを向上させるために、デフォルトのバッファサイズがのデフォルトよりはるかに大きくBufferedWriter定義されています。defaultBufferSizeれ、値65536が割り当てられています。

ここにコードがあります(ここでも簡潔さはここでは目標ではありません):

val defaultBufferSize: Int = 65536

def tryPrintToFile(
  lines: List[String],
  location: java.io.File,
  bufferSize: Int = defaultBufferSize
): scala.util.Try[Unit] = {
  tryUsingAutoCloseable(() => new java.io.FileWriter(location)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
    fileWriter =>
      tryUsingAutoCloseable(() => new java.io.BufferedWriter(fileWriter, bufferSize)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
        bufferedWriter =>
          tryUsingAutoCloseable(() => new java.io.PrintWriter(bufferedWriter)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
            printWriter =>
              scala.util.Try(
                lines.foreach(line => printWriter.println(line))
              )
          }
      }
  }
}

上記のtryPrintToFileメソッドはList[String]、入力としてa を取り、それをに送信するという点で便利ですFiletryWriteToFileでは、a Stringを受け取ってa に書き込むメソッドを作成しましょうFile

これがコードです(ここでは簡潔さの優先順位を推測させてください):

def tryWriteToFile(
  content: String,
  location: java.io.File,
  bufferSize: Int = defaultBufferSize
): scala.util.Try[Unit] = {
  tryUsingAutoCloseable(() => new java.io.FileWriter(location)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
    fileWriter =>
      tryUsingAutoCloseable(() => new java.io.BufferedWriter(fileWriter, bufferSize)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
        bufferedWriter =>
          Try(bufferedWriter.write(content))
      }
  }
}

最後に、aのコンテンツをFileとしてフェッチできると便利ですString。にscala.io.Sourceは、のコンテンツを簡単に取得するための便利なメソッドが用意されていますがFile、このcloseメソッドをで使用してSource、基盤となるJVMおよびファイルシステムのハンドルを解放する必要があります。これが行われない場合、リソースは、JVM GC(ガベージコレクター)がSourceインスタンス自体を解放できるようになるまで解放されません。そしてそれでも、finalizeメソッドがGCによってcloseリソースに呼び出されることを保証するのは、弱いJVMだけです。つまり、明示的にを呼び出すのはクライアントの責任です。これには、を処理するusingメソッドの2番目の定義が必要です。closeメソッドcloseインスタンスに背を向けるのはクライアントの責任と同じですjava.lang.AutoCloseablescala.io.Source

これのコードは次のとおりです(まだ簡潔ではありません)。

def tryUsingSource[S <: scala.io.Source, R]
  (instantiateSource: () => S)
  (transfer: S => scala.util.Try[R])
: scala.util.Try[R] =
  Try(instantiateSource())
    .flatMap(
      source => {
        var optionExceptionTry: Option[Exception] = None
        try
          transfer(source)
        catch {
          case exceptionTry: Exception =>
            optionExceptionTry = Some(exceptionTry)
            throw exceptionTry
        }
        finally
          try
            source.close()
          catch {
            case exceptionFinally: Exception =>
              optionExceptionTry match {
                case Some(exceptionTry) =>
                  exceptionTry.addSuppressed(exceptionFinally)
                case None =>
                  throw exceptionFinally
              }
          }
      }
    )

そして、これは非常に単純なラインストリーミングファイルリーダーでの使用例です(現在、データベース出力からタブ区切りファイルを読み取るために使用しています)。

def tryProcessSource(
    file: java.io.File
  , parseLine: (String, Int) => List[String] = (line, index) => List(line)
  , filterLine: (List[String], Int) => Boolean = (values, index) => true
  , retainValues: (List[String], Int) => List[String] = (values, index) => values
  , isFirstLineNotHeader: Boolean = false
): scala.util.Try[List[List[String]]] =
  tryUsingSource(scala.io.Source.fromFile(file)) {
    source =>
      scala.util.Try(
        ( for {
            (line, index) <-
              source.getLines().buffered.zipWithIndex
            values =
              parseLine(line, index)
            if (index == 0 && !isFirstLineNotHeader) || filterLine(values, index)
            retainedValues =
              retainValues(values, index)
          } yield retainedValues
        ).toList //must explicitly use toList due to the source.close which will
                 //occur immediately following execution of this anonymous function
      )
  )

上記の関数更新されたバージョンが、異なるが関連するStackOverflow質問への回答として提供されています。


これを、抽出されたインポートと一緒にまとめます(Eclipse ScalaIDEとIntelliJ Scalaプラグインの両方にあるScala Worksheetに貼り付けるのがはるかに簡単になり、出力をデスクトップにダンプしてテキストエディターでより簡単に調べることができます)。これは、コードがどのように見えるかを示します(簡潔さを増しました)。

import scala.io.Source
import scala.util.Try
import java.io.{BufferedWriter, FileWriter, File, PrintWriter}

val defaultBufferSize: Int = 65536

def tryUsingAutoCloseable[A <: AutoCloseable, R]
  (instantiateAutoCloseable: () => A) //parameter list 1
  (transfer: A => scala.util.Try[R])  //parameter list 2
: scala.util.Try[R] =
  Try(instantiateAutoCloseable())
    .flatMap(
      autoCloseable => {
        var optionExceptionTry: Option[Exception] = None
        try
          transfer(autoCloseable)
        catch {
          case exceptionTry: Exception =>
            optionExceptionTry = Some(exceptionTry)
            throw exceptionTry
        }
        finally
          try
            autoCloseable.close()
          catch {
            case exceptionFinally: Exception =>
              optionExceptionTry match {
                case Some(exceptionTry) =>
                  exceptionTry.addSuppressed(exceptionFinally)
                case None =>
                  throw exceptionFinally
              }
          }
      }
    )

def tryUsingSource[S <: scala.io.Source, R]
  (instantiateSource: () => S)
  (transfer: S => scala.util.Try[R])
: scala.util.Try[R] =
  Try(instantiateSource())
    .flatMap(
      source => {
        var optionExceptionTry: Option[Exception] = None
        try
          transfer(source)
        catch {
          case exceptionTry: Exception =>
            optionExceptionTry = Some(exceptionTry)
            throw exceptionTry
        }
        finally
          try
            source.close()
          catch {
            case exceptionFinally: Exception =>
              optionExceptionTry match {
                case Some(exceptionTry) =>
                  exceptionTry.addSuppressed(exceptionFinally)
                case None =>
                  throw exceptionFinally
              }
          }
      }
    )

def tryPrintToFile(
  lines: List[String],
  location: File,
  bufferSize: Int = defaultBufferSize
): Try[Unit] =
  tryUsingAutoCloseable(() => new FileWriter(location)) { fileWriter =>
    tryUsingAutoCloseable(() => new BufferedWriter(fileWriter, bufferSize)) { bufferedWriter =>
      tryUsingAutoCloseable(() => new PrintWriter(bufferedWriter)) { printWriter =>
          Try(lines.foreach(line => printWriter.println(line)))
      }
    }
  }

def tryWriteToFile(
  content: String,
  location: File,
  bufferSize: Int = defaultBufferSize
): Try[Unit] =
  tryUsingAutoCloseable(() => new FileWriter(location)) { fileWriter =>
    tryUsingAutoCloseable(() => new BufferedWriter(fileWriter, bufferSize)) { bufferedWriter =>
      Try(bufferedWriter.write(content))
    }
  }

def tryProcessSource(
    file: File,
  parseLine: (String, Int) => List[String] = (line, index) => List(line),
  filterLine: (List[String], Int) => Boolean = (values, index) => true,
  retainValues: (List[String], Int) => List[String] = (values, index) => values,
  isFirstLineNotHeader: Boolean = false
): Try[List[List[String]]] =
  tryUsingSource(() => Source.fromFile(file)) { source =>
    Try(
      ( for {
          (line, index) <- source.getLines().buffered.zipWithIndex
          values = parseLine(line, index)
          if (index == 0 && !isFirstLineNotHeader) || filterLine(values, index)
          retainedValues = retainValues(values, index)
        } yield retainedValues
      ).toList
    )
  }

Scala / FPの初心者として、私は何時間も費やして(主に頭を悩ませる欲求不満で)上記の知識と解決策を得ました。これが他のScala / FP初心者がこの特定の学習のこぶを早く乗り越えるのに役立つことを願っています。


2
信じられないほどのアップデート。唯一の問題は、100行程度のコードに置き換えられることtry-catch-finallyです。まだあなたの情熱が大好きです。
オブザーバー

1
@オブザーバー私はそれが不正確な声明であることを主張します。私が説明しているパターンは、AutoCloseablesのクローズの適切な処理を確実にするためにクライアントが書き込む必要があるボイラープレートの量を実際に減らし、同時にscala.util.Tryを使用するScalaの慣用的なFPパターンを有効にします。手動でtry / catch / finallyブロックを記述して同じ効果を達成しようとすると、想像以上に多くの定型文になってしまうでしょう。したがって、すべてのボイラープレートを100行のScala関数にプッシュすることには、大きな読みやすさの価値があります。
chaotic3quilibrium 2016

1
それが何らかの形で不快に聞こえた場合は申し訳ありません。それでも、このようなコードの量は必要ないというのが私のポイントです。これは、機能を持たないアプローチによってはるかに簡単に同じことを実現できるためです。個人的には、いくつかの追加のチェックを加えてtry-finallyと記述します。ただ短いだけです。ラッパーを使用したい場合は、ApacheUtilsを使用してすべてのダーティな作業を実行できます。さらに、すべての標準リーダー/ライターは基礎となるストリームを閉じるため、マルチラップは必要ありません。PS:投票をマイナス1からプラス1に変更して、あなたの努力をサポートします。ですから、悪意を持って私を疑わないでください。
オブザーバー

攻撃は行われません。
chaotic3quilibrium 2016

1
あなたの意見を理解しています。議論をありがとう、私はそれについて少し考えなければなりません。ごきげんよう!
オブザーバー

3

scalaz-streamを使用してファイルにいくつかの行を書き込む例を次に示します

import scalaz._
import scalaz.stream._

def writeLinesToFile(lines: Seq[String], file: String): Task[Unit] =
  Process(lines: _*)              // Process that enumerates the lines
    .flatMap(Process(_, "\n"))    // Add a newline after each line
    .pipe(text.utf8Encode)        // Encode as UTF-8
    .to(io.fileChunkW(fileName))  // Buffered write to the file
    .runLog[Task, Unit]           // Get this computation as a Task
    .map(_ => ())                 // Discard the result

writeLinesToFile(Seq("one", "two"), "file.txt").run

1

samthebestと彼の前の貢献者を上回るために、私は名前と簡潔さを改善しました:

  def using[A <: {def close() : Unit}, B](resource: A)(f: A => B): B =
    try f(resource) finally resource.close()

  def writeStringToFile(file: File, data: String, appending: Boolean = false) =
    using(new FileWriter(file, appending))(_.write(data))

これは、反射に依存する「ダックタイピング」を使用します。多くのコンテキストでは、リフレクションに依存することは重要ではありません。
chaotic3quilibrium 2018年

1

依存関係なし、エラー処理あり

  • 標準ライブラリのメソッドのみを使用します
  • 必要に応じて、ファイルのディレクトリを作成します
  • Eitherエラー処理の使用

コード

def write(destinationFile: Path, fileContent: String): Either[Exception, Path] =
  write(destinationFile, fileContent.getBytes(StandardCharsets.UTF_8))

def write(destinationFile: Path, fileContent: Array[Byte]): Either[Exception, Path] =
  try {
    Files.createDirectories(destinationFile.getParent)
    // Return the path to the destinationFile if the write is successful
    Right(Files.write(destinationFile, fileContent))
  } catch {
    case exception: Exception => Left(exception)
  }

使用法

val filePath = Paths.get("./testDir/file.txt")

write(filePath , "A test") match {
  case Right(pathToWrittenFile) => println(s"Successfully wrote to $pathToWrittenFile")
  case Left(exception) => println(s"Could not write to $filePath. Exception: $exception")
}

1

2019アップデート:

まとめ-Java NIO(または非同期の場合はNIO.2)は、依然としてScalaでサポートされている最も包括的なファイル処理ソリューションです。次のコードは、テキストを作成して新しいファイルに書き込みます。

import java.io.{BufferedOutputStream, OutputStream}
import java.nio.file.{Files, Paths}

val testFile1 = Paths.get("yourNewFile.txt")
val s1 = "text to insert in file".getBytes()

val out1: OutputStream = new BufferedOutputStream(
  Files.newOutputStream(testFile1))

try {
  out1.write(s1, 0, s1.length)
} catch {
  case _ => println("Exception thrown during file writing")
} finally {
  out1.close()
}
  1. Javaライブラリのインポート:IOおよびNIO
  2. Path選択したファイル名でオブジェクトを作成します
  3. ファイルに挿入するテキストをバイト配列に変換します
  4. ファイルをストリームとして取得します。 OutputStream
  5. バイト配列を出力ストリームのwrite関数に渡します
  6. ストリームを閉じる

1

この回答と同様に、これfs2(バージョン1.0.4)の例です。

import cats.effect._

import fs2._
import fs2.io

import java.nio.file._

import scala.concurrent.ExecutionContext
import scala.language.higherKinds
import cats.syntax.functor._

object ScalaApp extends IOApp {

  def write[T[_]](p: Path, s: String)
                 (implicit F: ConcurrentEffect[T], cs: ContextShift[T]): T[Unit] = {
    Stream(s)
      .covary[T]
      .through(text.utf8Encode)
      .through(
        io.file.writeAll(
          p,
          scala.concurrent.ExecutionContext.global,
          Seq(StandardOpenOption.CREATE)
        )
      )
      .compile
      .drain
  }


  def run(args: List[String]): IO[ExitCode] = {

    implicit val executionContext: ExecutionContext =
      scala.concurrent.ExecutionContext.Implicits.global

    implicit val contextShift: ContextShift[IO] =
      IO.contextShift(executionContext)

    val outputFile: Path = Paths.get("output.txt")

    write[IO](outputFile, "Hello world\n").as(ExitCode.Success)

  }
}

0

この行は、配列または文字列からファイルを書き込むのに役立ちます。

 new PrintWriter(outputPath) { write(ArrayName.mkString("")); close }

0

とにかくプロジェクトにAkkaストリームを使用している場合は、ワンライナーが提供されます。

def writeToFile(p: Path, s: String)(implicit mat: Materializer): Unit = {
  Source.single(ByteString(s)).runWith(FileIO.toPath(p))
}

Akka docs> ストリーミングファイルIO

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.