Scala:文字列を1つのステートメントでファイルに書き込む


144

Scalaでファイルを読み取るには、

Source.fromFile("file.txt").mkString

文字列をファイルに書き込む同等で簡潔な方法はありますか?

ほとんどの言語はそのようなものをサポートしています。私のお気に入りはGroovyです。

def f = new File("file.txt")
// Read
def s = f.text
// Write
f.text = "file contents"

1行からコードの短いページまでのプログラムにコードを使用したいと思います。ここでは、独自のライブラリを使用する必要はありません。現代の言語では、何かをファイルに簡単に書き込むことができると思います。

これに似た投稿がありますが、それらは私の正確な質問に答えないか、古いScalaバージョンに焦点を当てています。

例えば:


この質問を参照してください。最高評価の回答に同意します-個人用のライブラリを用意することをお勧めします。
ffriend 2011

2
この場合、私は自分の個人的なライブラリを書かなければならないことに同意しません。通常、その場しのぎのことを行う小さなプログラムを作成している場合(たぶん、それを単一ページのscalaスクリプトとして、または単にREPLで記述したいだけかもしれません)。次に、個人用ライブラリにアクセスするのは面倒になります。
smartnut007 2011

基本的に、現時点ではscala 2.9には何もないようです。この質問を開いたままにする必要があるかどうかはわかりません。
smartnut007 2011

1
Scalaソースコードでjava.ioを検索すると、多くのオカレンスは見つかりません。出力操作、特にPrintWriterのオカレンスはさらに少なくなります。そのため、Scala-IOライブラリがScalaの公式部分になるまで、パラダイムで示されているように、純粋なJavaを使用する必要があります。
PhiLho 2011

ええ、probにはjdk7 IOの改善と互換性のあるscala-ioも必要です。
smartnut007 2011

回答:


77

簡潔な1行:

import java.io.PrintWriter
new PrintWriter("filename") { write("file contents"); close }

14
このアプローチは見栄えは良いですが、例外セーフでもエンコードセーフでもありません。で例外が発生した場合write()closeは呼び出されず、ファイルは閉じられません。PrintWriterまた、移植性が非常に悪いデフォルトのシステムエンコーディングを使用します。そして最後に、このアプローチはこの行専用の個別のクラスを生成します(ただし、Scalaは単純なコードでもすでに大量のクラスを生成しているので、これ自体は欠点ではありません)。
Vladimir

上記のコメントによると、これは1つのライナーですが、安全ではありません。:あなたは場所の周りに、より多くのオプションを有し、かつ/または入力をバッファリングしながら、より安全性が必要な場合、私はちょうど似たスレッドに掲載の答えを参照stackoverflow.com/a/34277491/501113
chaotic3quilibrium

2
一時的なデバッグ・ロギングのための私が使用new PrintWriter(tmpFile) { try {write(debugmsg)} finally {close} }
ランドール・ホイットマン

文体的にはおそらく使用した方が良いclose()と思います
Casey

これは、二つのラインであり、容易にこのような1行にすることができます:new java.io.PrintWriter("/tmp/hello") { write("file contents"); close }
Philluminati

163

誰もNIO.2操作(Java 7以降で利用可能)を提案していなかったことは奇妙です。

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

Files.write(Paths.get("file.txt"), "file contents".getBytes(StandardCharsets.UTF_8))

これははるかに単純で最も簡単で最も慣用的な方法であり、Java自体に依存する必要はありません。


これは、何らかの理由で改行文字を無視します。
カカジ2016年

@カカジさん、詳しく教えていただけますか?改行付きの文字列でテストしたところ、問題なく動作しました。それは何もフィルタリングできません-Files.write()はバイトアレイをBLOBとして書き込み、その内容を分析しません。結局のところ、一部のバイナリデータでは、0x0dバイトが改行以外の重要な意味を持つ場合があります。
Vladimir Matveev 2016年

4
追加の注記:ファイルがある場合は、「。toPath」だけで最初のパラメーターを取得します。
akauppi 2016年

1
これは(明示的なCharSetsの選択により)より工業グレードですが、の単純さ(および1つのライナー)に欠けていreflect.io.File.writeAll(contents)ます。どうして?2つのインポート文を含めると3行。IDEが自動的に行うかもしれませんが、REPLの場合はそれほど簡単ではありません。
javadba 2017年

3
@javadba私たちはJVMにいるので、インポートはほとんど「ライン」としてカウントされません。これは、代替手段がほとんど常に新しいライブラリの依存関係を追加しているためです。とにかく、Files.writeまた受け入れるjava.lang.Iterable2番目の引数として、私たちはScalaのからのことを得ることができますIterable:コンバーターを使用して、任意のコレクション型、つまりかなり多く、import java.nio.file.{Files, Paths}; import scala.collection.JavaConverters.asJavaIterableConverter; val output = List("1", "2", "3"); Files.write(Paths.get("output.txt"), output.asJava)
Yawar

83

以下は、reflect.io.fileを使用した簡潔な1行です。これはScala 2.12で動作します。

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

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

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

1
+1すばらしい。Some/ foreachコンボは、ビットファンキーですが、それは最終的にノーのtry / catch /の利点が付属しています。
ブレントファウスト2013年

3
書き込みが例外をスローする場合、例外から回復してファイルの読み取り/書き込みを再度行う予定がある場合は、ファイルを閉じることができます。幸いにも、scalaはこれを行うためのワンライナーを提供します。
Garrett Hall

25
scala.tools.nsc.ioパッケージはパブリックAPIの一部ではなく、コンパイラーによって使用されるため、これは推奨されません。
ジョヴァンニボッタ2014

3
Some/ foreach多くの人々は、それが生成するためにハッカーを引き起こし読めないコードのためのScalaのを嫌い、なぜハックは正確です。
エリックカプルン2014

3
scala.tootls.nsc.io.Fileのエイリアスですreflect.io.File。まだ内部APIですが、少なくとももう少し短いです。
kostja

41

Groovy構文が好きな場合は、Pimp-My-Library設計パターンを使用してScalaに取り込むことができます。

import java.io._
import scala.io._

class RichFile( file: File ) {

  def text = Source.fromFile( file )(Codec.UTF8).mkString

  def text_=( s: String ) {
    val out = new PrintWriter( file , "UTF-8")
    try{ out.print( s ) }
    finally{ out.close }
  }
}

object RichFile {

  implicit def enrichFile( file: File ) = new RichFile( file )

}

期待どおりに動作します:

scala> import RichFile.enrichFile
import RichFile.enrichFile

scala> val f = new File("/tmp/example.txt")
f: java.io.File = /tmp/example.txt

scala> f.text = "hello world"

scala> f.text
res1: String = 
"hello world

2
Source.fromFileで返されたインスタンスでcloseを呼び出すことはありません。つまり、リソースがGCed(ガベージコレクション)になるまでリソースは閉じられません。また、PrintWriterはバッファーされないため、8バイトの小さなJVMデフォルトバッファーを使用しているため、IOが大幅に遅くなる可能性があります。これらの問題に対処する同様のスレッドで回答を作成しました:stackoverflow.com/a/34277491/501113
chaotic3quilibrium

1
あなたが正しいです。しかし、ここで与えた解決策は、IOが小さい短期間のプログラムに適しています。サーバープロセスや大きなデータ(おおよその目安として500 MB以上)にはお勧めしません。
パラダイム

23
import sys.process._
"echo hello world" #> new java.io.File("/tmp/example.txt") !

REPLでは私にはうまくいきません。エラーメッセージは表示されませんが、/ tmp / example.txtを見ると表示されません。
ユーザー不明の

@user unknown、 '!'を逃して申し訳ありません コマンドの最後で、今修正されました。
xiefei

素晴らしい!うまくいったので、その理由を知りたいのですが。とは何ですか#>、何をし!ますか?
ユーザー不明の

10
このソリューションはポータブルではありません!* nixシステムでのみ機能します。
Giovanni Botta 2014年

2
これは、任意の文字列を書き込む場合には機能しません。コマンドラインechoツールに引数として渡すことができる短い文字列に対してのみ機能します。
リッチ

15

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

file.write("Hi!")

または

file << "Hi!"

3
あなたのライブラリが大好きです!この質問は、scalaを使用してファイルを書き込む方法をグーグルで検索するときのトップヒットの1つです。プロジェクトが大きくなったので、答えを少し拡張することをお勧めします。
asac

12

Apache File Utilsを簡単に使用できます。関数を見てくださいwriteStringToFile。私たちはこのライブラリをプロジェクトで使用しています。


3
私もずっと使っています。質問を注意深く読んだ場合、私はすでにライブラリを使用したくない理由をすでに知っています。
smartnut007

ライブラリを使用せずに、私は、読み取り/書き込み時のハンドルの例外そのソリューションを作成し、小さなJavaライブラリが提供するバッファのデフォルト値を超えてバッファリングすることができるようですしている:stackoverflow.com/a/34277491/501113
chaotic3quilibrium

7

1つはこの形式もあり、簡潔であり、基礎となるライブラリは美しく書かれています(ソースコードを参照)。

import scalax.io.Codec
import scalax.io.JavaConverters._

implicit val codec = Codec.UTF8

new java.io.File("hi-file.txt").asOutput.write("I'm a hi file! ... Really!")

7

これは十分簡潔だと思います:

scala> import java.io._
import java.io._

scala> val w = new BufferedWriter(new FileWriter("output.txt"))
w: java.io.BufferedWriter = java.io.BufferedWriter@44ba4f

scala> w.write("Alice\r\nBob\r\nCharlie\r\n")

scala> w.close()

4
十分に公平ですが、この「簡潔さ」は「1つのステートメント」として分類されません:P
Erik Kaplun '16

このコードは、Javaの認識されている問題の多くを保持しています。残念ながら、ScalaはIOを十分に重要視していないため、標準ライブラリにはIOが含まれていません。
クリス

答えは、新しいFileWriterの孤立したリソースの問題を隠します。新しいFileWriterは成功したが、新しいBufferedWriterが失敗した場合、FileWriterインスタンスは二度と表示されず、GCed(Garbage Collected)まで開いたままであり、それでも閉じられない可能性があります(ファイナライズ保証がJVMで機能するため)。これらの問題に対処する同様の質問への回答を書きました:stackoverflow.com/a/34277491/501113
chaotic3quilibrium

2

これは、JavaライブラリーとScalaライブラリーを組み合わせて行うことができます。文字エンコーディングを完全に制御できます。しかし、残念ながら、ファイルハンドルは適切に閉じられません。

scala> import java.io.ByteArrayInputStream
import java.io.ByteArrayInputStream

scala> import java.io.FileOutputStream
import java.io.FileOutputStream

scala> BasicIO.transferFully(new ByteArrayInputStream("test".getBytes("UTF-8")), new FileOutputStream("test.txt"))

コードに孤立したリソースインスタンスの問題があります。呼び出しの前にインスタンスをキャプチャしていないため、呼び出すメソッドにパラメータが渡される前にいずれかの例外がスローされた場合、正常にインスタンス化されたリソースは、GCed(ガベージコレクション)、さらにはその場合、GC保証の動作方法が原因ではない可能性があります。これらの問題に対処する同様の質問への回答を書きました:stackoverflow.com/a/34277491/501113
chaotic3quilibrium

1
あなたは正しいとあなたの解決策は非常にいいです。しかし、ここでの要件は、それを1行で実行することでした。そして、あなたが注意深く読んだとき、私は私の回答でのリソースリークを、この要件と私のアプローチに伴う制限として述べました。あなたの解決策は素晴らしいですが、その1行の要件に一致しません。
stefan.schwetschke 2015

2

1行ではないことはわかっていますが、私が知る限り、安全上の問題を解決します。

// This possibly creates a FileWriter, but maybe not
val writer = Try(new FileWriter(new File("filename")))
// If it did, we use it to write the data and return it again
// If it didn't we skip the map and print the exception and return the original, just in-case it was actually .write() that failed
// Then we close the file
writer.map(w => {w.write("data"); w}).recoverWith{case e => {e.printStackTrace(); writer}}.map(_.close)

例外処理を気にしていない場合は、次のように書くことができます

writer.map(w => {w.writer("data"); w}).recoverWith{case _ => writer}.map(_.close)

1

更新:その後、私はここで詳しく説明したより効果的なソリューションを作成しました:https : //stackoverflow.com/a/34277491/501113

Scala IDE for EclipseのScala Worksheetで作業することが増えています(IntelliJ IDEAにも同等のものがあると思います)。とにかく、「出力がカットオフ制限を超えています」というメッセージが表示されたら、一部のコンテンツを出力するためにワンライナーを実行できるようにする必要があります。特にScalaコレクションで重要なことをしている場合のメッセージ。

これを簡素化するために、新しいScalaワークシートのそれぞれの上部に挿入する1行のコードを思いつきました(そのため、非常に単純な必要性のために外部ライブラリのインポート全体を実行する必要はありません)。あなたが執事であり、それが技術的に2行であることに気付いた場合、それはこのフォーラムで読みやすくするためだけです。私のScalaワークシートの1行です。

def printToFile(content: String, location: String = "C:/Users/jtdoe/Desktop/WorkSheet.txt") =
  Some(new java.io.PrintWriter(location)).foreach{f => try{f.write(content)}finally{f.close}}

そして使い方は簡単です:

printToFile("A fancy test string\ncontaining newlines\nOMG!\n")

これにより、デフォルト(メソッドが呼び出されるたびにファイルが完全に上書きされる)を超える追加のファイルが必要な場合に、オプションでファイル名を指定できます。

したがって、2番目の使用法は単純です。

printToFile("A fancy test string\ncontaining newlines\nOMG!\n", "C:/Users/jtdoe/Desktop/WorkSheet.txt")

楽しい!


1

アンモナイトopsライブラリを使用します。構文は非常に最小限ですが、ライブラリの幅は、bashなどのシェルスクリプト言語でそのようなタスクを試行することで期待されるものとほぼ同じです。

リンクしたページには、ライブラリで実行できる多くの操作が表示されていますが、この質問に答えるために、これはファイルへの書き込みの例です

import ammonite.ops._
write(pwd/'"file.txt", "file contents")

-1

セミコロンの魔法を通して、ワンライナーのようなものを作ることができます。

import java.io.PrintWriter
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.charset.StandardCharsets
import java.nio.file.StandardOpenOption
val outfile = java.io.File.createTempFile("", "").getPath
val outstream = new PrintWriter(Files.newBufferedWriter(Paths.get(outfile)
  , StandardCharsets.UTF_8
  , StandardOpenOption.WRITE)); outstream.println("content"); outstream.flush(); outstream.close()

ここでは議論はありません。私は自分の人生の一部をフラッシュするためにどのAPIが必要かを思い出さないようにしたので、いつもそれを行います。
Ion Freeman
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.