Для чтения есть полезная абстракция Source
. Как написать строки в текстовый файл?
Как записать файл в Scala?
Ответ 1
Править 2019 год (8 лет спустя), поскольку Scala-IO не очень активен, если таковой имеется, Ли Хаои предлагает свою собственную библиотеку lihaoyi/os-lib
, которую он представляет ниже.
Июнь 2019 года Ксавье Гихот упоминает в своем ответе библиотеку Using
, утилиту для автоматического управления ресурсами.
Редактирование (сентябрь 2011 г.): с тех пор, как Эдуардо Коста спрашивает о Scala2.9, и с тех пор, как Рик-777 комментирует, что история коммитов scalax.IO практически отсутствует с середины 2009 года...
Scala-IO изменил место: посмотрите его репозиторий GitHub от Джесси Эйхара (также на SO):
Зонтик-проект Scala IO состоит из нескольких подпроектов для различных аспектов и расширений IO.
Существует два основных компонента Scala IO:
- Core - Core в основном занимается чтением и записью данных в произвольные источники и приемники и из них. Основными чертами являются
Input
,Output
иSeekable
которые обеспечивают базовый API.
Другими важными классами являютсяResource
,ReadChars
иWriteChars
.- Файл - Файл - это API-интерфейс
File
(называемыйPath
), основанный на комбинации файловой системы Java 7 NIO и API-интерфейсов SBT PathFinder.Path
иFileSystem
являются основными точками входа в Scala IO File 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 г.) со старым местом для scala-io:
Если вы не хотите ждать Scala2.9, вы можете использовать библиотеку scala-инкубатор/scala-io.
(как упоминалось в разделе " Почему источник Scala не закрывает базовый 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)
}
Ответ 2
Это одна из функций, отсутствующих в стандартном Scala, который я нашел настолько полезным, что добавляю его в свою личную библиотеку. (Вероятно, у вас тоже должна быть личная библиотека.) Код выглядит так:
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)
}
Ответ 3
Как и ответ Рекса Керра, но более общий. Сначала я использую вспомогательную функцию:
/**
* 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)
}
}
и др.
Ответ 4
Простой ответ:
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()
}
Ответ 5
Дайте другой ответ, потому что мои правки других ответов отклоняются.
Это самый сжатый и простой ответ (похожий на Garret Hall's)
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
или lambdas, а также использование синтаксиса placeholder. Также обратите внимание на лучшее именование.
Ответ 6
Вот краткий однострочный текст с использованием библиотеки компилятора Scala:
scala.tools.nsc.io.File("filename").writeAll("hello world")
В качестве альтернативы, если вы хотите использовать библиотеки Java, вы можете сделать это взломать:
Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
Ответ 7
Один вкладыш для сохранения/чтения в/из String
, используя 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
Ответ 8
Микробиблиотека, которую я написал: https://github.com/pathikrit/better-files
file.appendLine("Hello", "World")
или
file << "Hello" << "\n" << "World"
Ответ 9
ОБНОВЛЕНИЕ 2019/сентябрь/01:
- Начиная с Scala 2.13, предпочитайте использовать scala.util.Using
- Исправлена ошибка, из-за которой
finally
глотал оригинальныйException
, брошенныйtry
, если кодfinally
бросилException
Изучив все эти ответы о том, как легко написать файл в Scala, и некоторые из них довольно хороши, у меня возникло три вопроса:
- В ответе Jus12 использование каррирования для использования вспомогательного метода не очевидно для начинающих Scala/FP
- Необходимо инкапсулировать ошибки более низкого уровня с помощью
scala.util.Try
- Необходимо показать Java-разработчикам, плохо знакомым с Scala/FP, как правильно вкладывать зависимые ресурсы, чтобы метод
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
может сбивать с толку, потому что у него два списка параметров вместо обычного списка с одним параметром. Это называется карри. И я не буду вдаваться в подробности, как работает карри или где это иногда полезно. Оказывается, что для этой конкретной проблемной области, это правильный инструмент для работы.
Затем нам нужно создать метод, 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]
в качестве входных данных и отправляет его в File
. Теперь давайте создадим метод tryWriteToFile
, который берет String
и записывает его в 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))
}
}
}
Наконец, полезно иметь возможность извлекать содержимое File
как String
. Хотя scala.io.Source
предоставляет удобный метод для простого получения содержимого File
, метод close
должен использоваться в Source
для освобождения базовых дескрипторов JVM и файловой системы. Если этого не сделать, ресурс не будет освобожден, пока JCM GC (сборщик мусора) не сможет освободить сам экземпляр Source
. И даже в этом случае слабая JVM гарантирует, что метод finalize
будет вызван GC для close
ресурса. Это означает, что клиент несет ответственность за явный вызов метода close
, точно так же, как это обязанность клиента - высвободить close
на экземпляре java.lang.AutoCloseable
. Для этого нам понадобится второе определение метода using, который обрабатывает 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
)
)
Обновленная версия вышеупомянутой функции была предоставлена в качестве ответа на другой, но связанный вопрос Кару.
Теперь, собрав все это вместе с извлеченным импортом (значительно упростив вставку в рабочую таблицу Scala, присутствующую как в Eclipse ScalaIDE, так и в плагине IntelliJ Scala, чтобы упростить выгрузку вывода на рабочий стол для более удобного изучения в текстовом редакторе), Вот как выглядит код (с повышенной краткостью):
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 быстрее справиться с этой проблемой.
Ответ 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")
Он имеет одну строку для записи в файлы, добавления в файлы, перезаписи файлов и многих других полезных/общих операций
Ответ 11
Здесь приведен пример записи некоторых строк в файл с помощью 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
Ответ 12
Начиная с 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")) }
Ответ 13
Чтобы превзойти 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))
Ответ 14
Без зависимостей, с обработкой ошибок
- Использует только методы из стандартной библиотеки
- Создает каталоги для файла, если необходимо
- Использует
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")
}
Ответ 15
Если вы в любом случае имеете Akka Streams в своем проекте, он предоставляет однострочный:
def writeToFile(p: Path, s: String)(implicit mat: Materializer): Unit = {
Source.single(ByteString(s)).runWith(FileIO.toPath(p))
}
Akka docs> Streaming File IO
Ответ 16
2019 Обновление:
Резюме - Java NIO (или NIO.2 для async) по-прежнему является наиболее полным решением для обработки файлов, поддерживаемым в 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()
}
- Импорт библиотек Java: IO и NIO
- Создайте объект
Path
с выбранным вами именем файла - Преобразуйте ваш текст, который вы хотите вставить в файл, в байтовый массив
- Получите ваш файл в виде потока:
OutputStream
- Передайте ваш байтовый массив в вашу функцию
write
потока вывода - Закрыть поток
Ответ 17
Аналогично этому ответу, вот пример с 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)
}
}
Ответ 18
Эта строка помогает записать файл из массива или строки.
new PrintWriter(outputPath) { write(ArrayName.mkString("")); close }