Для чтения есть полезная абстракция Source
. Как я могу записать строки в текстовый файл?
Для чтения есть полезная абстракция Source
. Как я могу записать строки в текстовый файл?
Ответы:
Править 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
.- Файл - файл - это
File
(называемыйPath
) API, основанный на комбинации файловой системы 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)
}
Это одна из функций, отсутствующих в стандартной версии 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)
}
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
из?
Дать другой ответ, потому что мои правки других ответов были отклонены.
Это самый краткий и простой ответ (похожий на ответ Гаррета Холла)
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
, используя 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
один.
К сожалению для лучшего ответа, 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 / сентябрь / 01:
finally
проглатывался оригинальный Exception
брошенный код, try
еслиfinally
Exception
Изучив все эти ответы о том, как легко написать файл в Scala, и некоторые из них довольно хороши, у меня возникло три вопроса:
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
метода может сбить с толку, потому что он, кажется, имеет два списка параметров вместо обычного списка с одним параметром. Это называется карри. И я не буду вдаваться в подробности, как работает карри или где это иногда полезно. Оказывается, для этой конкретной проблемной области это правильный инструмент для работы.
Далее нам нужно создать метод, tryPrintToFile
который создаст (или перезапишет существующий) File
и напишет List[String]
. Он использует a, FileWriter
инкапсулированный a, BufferedWriter
который, в свою очередь, инкапсулируется a 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))
}
}
}
Наконец, полезно иметь возможность извлекать содержимое a File
как a String
. В то время как scala.io.Source
предоставляет удобный метод для простого получения содержимого File
, close
метод должен использоваться в, Source
чтобы выпустить основные JVM и дескрипторы файловой системы. Если этого не сделать, ресурс не будет освобожден, пока JVM 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
)
)
Обновленная версия вышеупомянутой функции была предоставлена в ответ на различный , но связанный StackOverflow вопрос .
Теперь, собрав все это вместе с извлеченным импортом (упрощая вставку в рабочую таблицу 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 быстрее справиться с этой проблемой.
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 для 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()
}
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 Streams в вашем проекте, он предоставляет одну строку:
def writeToFile(p: Path, s: String)(implicit mat: Materializer): Unit = {
Source.single(ByteString(s)).runWith(FileIO.toPath(p))
}
Akka docs> Потоковый файл IO