読むために、便利な抽象化がありSource
ます。テキストファイルに行を書き込むにはどうすればよいですか?
読むために、便利な抽象化がありSource
ます。テキストファイルに行を書き込むにはどうすればよいですか?
回答:
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つの主要なコンポーネントがあります。
- コア -コアは主に、任意のソースおよびシンクとの間でデータを読み書きすることを扱います。コーナーストーンの特徴は
Input
でOutput
ありSeekable
、コアAPIを提供します。
他の重要なクラスはResource
、ReadChars
かつ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)
}
これは、標準の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)
}
Source
(デフォルトはデフォルトのエンコーディング)。もちろん、これが一般的なニーズであるとenc: Option[String] = None
わかったf
場合は、たとえばパラメータを後に追加できます。
レックスカーの回答に似ていますが、より一般的です。まず、ヘルパー関数を使用します。
/**
* 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)
}
}
等
簡単な答え:
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()
}
import
か?
他の回答の編集は拒否されたため、別の回答を与えます。
これは最も簡潔でシンプルな答えです(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
やラムダは不要であり、プレースホルダー構文の使用法に注意してください。また、より良い命名に注意してください。
implemented
前提条件を満たしていません。実装されていないコードは使用できません。それはデフォルトでは利用できず、よく知られていませんので、それを見つける方法を伝える必要があることを意味します。
以下は、Scalaコンパイラライブラリを使用した簡潔なワンライナーです。
scala.tools.nsc.io.File("filename").writeAll("hello world")
または、Javaライブラリを使用する場合は、このハックを実行できます。
Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
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
write
コピーcontents
するため、ピーク時には、contents
単独の2倍のメモリを使用します。
残念ながら、トップの回答では、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")
それはのためのワンライナー持つファイルへの書き込み、ファイルに追加する、ファイルを上書きし、そして他の多くの有益な/一般的な操作を
私が書いたマイクロライブラリ:https : //github.com/pathikrit/better-files
file.appendLine("Hello", "World")
または
file << "Hello" << "\n" << "World"
以降Scala 2.13
、標準ライブラリは専用のリソース管理ユーティリティを提供します。Using
。
この場合、PrintWriter
またはなどのリソースで使用でき、ファイルに書き込むためにBufferedWriter
拡張さAutoCloseable
れます。何があっても、後でリソースを閉じます。
たとえば、java.io
api:
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.nio
apiを使用:
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"))
}
2019年9月1日の更新:
finally
オリジナルを飲み込むバグを修正Exception
try
finally
Exception
Scalaでファイルを簡単に書き込む方法に関するこれらすべての回答を確認したところ、そのうちのいくつかは非常に優れていて、3つの問題がありました。
scala.util.Try
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 を取り、それをに送信するという点で便利ですFile
。tryWriteToFile
では、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.AutoCloseable
scala.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初心者がこの特定の学習のこぶを早く乗り越えるのに役立つことを願っています。
try-catch-finally
です。まだあなたの情熱が大好きです。
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
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))
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")
}
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()
}
Path
選択したファイル名でオブジェクトを作成しますOutputStream
write
関数に渡しますこの回答と同様に、これは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)
}
}
とにかくプロジェクトにAkkaストリームを使用している場合は、ワンライナーが提供されます。
def writeToFile(p: Path, s: String)(implicit mat: Materializer): Unit = {
Source.single(ByteString(s)).runWith(FileIO.toPath(p))
}
Akka docs> ストリーミングファイルIO