Для читання є корисна абстракція Source
. Як я можу записати рядки до текстового файлу?
Для читання є корисна абстракція Source
. Як я можу записати рядки до текстового файлу?
Відповіді:
Редагуйте 2019 (через 8 років), коли Скала-ІО не дуже активний, якщо такий є, Лі Хаої пропонує свою власну бібліотеку lihaoyi/os-lib
, яку він подає нижче .
У червні 2019 року Ксав'є Гіхот у своїй відповіді згадує бібліотеку Using
, утиліту для автоматичного управління ресурсами.
Редагувати (вересень 2011 р.): Оскільки Едуардо Коста запитує про Scala2.9, а після того, як Rick-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
є основними вхідними точками в API файлу Scala IO.
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- inkubator / 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
і запишемо a List[String]
. Він використовує, 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
метод, який бере a String
і записує його в aFile
.
Ось код (і я дозволю вам здогадатися про пріоритет лаконічності тут):
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 та файлової системи. Якщо цього не зробити, то ресурс не буде випущений, поки JVM GC (Garbage Collector) не обійдеться, щоб випустити сам Source
екземпляр. І навіть тоді є лише слабка гарантія JVM . Для цього нам потрібно друге визначення використовуваного методу, який обробляєfinalize
, що GC метод буде викликаний до close
ресурсу. Це означає, що клієнт несе відповідальність за явний виклик телефону .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 питання .
Тепер, зробивши все це разом із витягнутим імпортом (полегшує вставку в робочий аркуш 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> IO потокового файлу