Как написать в файл в Scala?
для чтения есть полезная абстракция Source
. Как я могу писать строки в текстовый файл?
14 ответов:
редактировать (сентябрь 2011): с Эдуардо Коста спрашивает о Scala2. 9, а так как Рик-777 комментарии, что scalax.IO совершить историю практически не существует с середины 2009 года...
Scala-IO изменилось место: см. Его GitHub repo С Джесси Eichar (кроме так):
зонтичный проект Scala IO состоит из несколько подпроектов для различных аспектов и расширений ввода-вывода.
Есть два основных компонента Scala IO:
- базовый - Core в основном занимается чтением и записью данных в произвольные источники и приемники. Краеугольный камень черты
Input
,Output
иSeekable
которые обеспечивают основной API.
Другие классы важности являютсяResource
,ReadChars
иWriteChars
.- File - файл-это
File
(называетсяPath
) API, который основан на комбинации Java 7 NIO filesystem и SBT Pathfinder API.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) }
похоже на ответ Рекса Керра, но более общий. Сначала я использую вспомогательную функцию:
/** * 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) } }
etc.
простой ответ:
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() }
давая другой ответ, потому что мои правки других ответов, где отклонены.
Это самый краткий и простой ответ (похоже на мансарду Холла)
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
, ни лямбды, и обратите внимание на использование синтаксиса заполнителя. Также обратите внимание на лучшее именование.
один лайнер для сохранения / чтения в/из
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.НИО.файл.Файлы.пиши
java.ленг.Строка.getBytes
скала.коллекция.JavaConverters
скала.коллекция.неизменный.Список.mkString
вот краткое однострочное использование библиотеки компилятора Scala:
scala.tools.nsc.io.File("filename").writeAll("hello world")
кроме того, если вы хотите использовать Java-библиотеки, вы можете сделать этот хак:
Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
микро библиотека я написал:https://github.com/pathikrit/better-files
file.appendLine("Hello", "World")
или
file << "Hello" << "\n" << "World"
после просмотра всех этих ответов о том, как легко написать файл в 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 => try transfer(autoCloseable) finally autoCloseable.close() )
начало выше
tryUsingAutoCloseable
метод может быть запутанным, потому что он, кажется, имеет два списка параметров вместо обычного списка одного параметра. Это называется подлизывающийся. И я не буду вдаваться в подробности, как работает карринг или где он находится иногда полезное. Оказывается, что для этого конкретного проблемного пространства это правильный инструмент для работы.Далее, нам нужно создать метод,
tryPrintToFile
, который создаст (или перезапишет существующий)File
и писатьList[String]
. Он используетFileWriter
который инкапсулируется с помощьюBufferedWriter
который в свою очередь инкапсулируется aPrintWriter
. И чтобы повысить производительность, размер буфера по умолчанию много больше чем значение по умолчанию для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 и файловой системы. Если это не сделано, то ресурс не освобождается до тех пор, пока JVM GC (сборщик мусора) не приступит к освобождениюSource
сам экземпляр. И даже тогда, есть только слабая гарантия JVMfinalize
метод будет вызван GC toclose
ресурсе. Это означает, что клиент несет ответственность за явный вызовclose
метод, так же, как это ответственность клиента перед tallclose
на примере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 => try transfer(source)) finally source.close() )
и вот пример его использования в супер простой строке потокового чтения файлов (в настоящее время используется для чтения файлов с разделителями табуляции из вывода базы данных):
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 ) )
An обновленная версия вышеуказанной функции был предоставлен в качестве ответа на A другой, но связанный с 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)(transfer: A => scala.util.Try[R]): scala.util.Try[R] = Try(instantiateAutoCloseable()) .flatMap( autoCloseable => try transfer(autoCloseable)) finally autoCloseable.close() ) def tryUsingSource[S <: scala.io.Source, R] (instantiateSource: () => S)(transfer: S => scala.util.Try[R]): scala.util.Try[R] = Try(instantiateSource()) .flatMap( source => try transfer(source)) finally source.close() ) 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 быстрее преодолеть этот конкретный учебный горб.
вот пример записи некоторых строк в файл с помощью 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") }
эта строка помогает записать файл из массива или строки.
new PrintWriter(outputPath) { write(ArrayName.mkString("")); close }
Если у вас в любом случае есть потоки Akka в вашем проекте, он предоставляет однострочный:
def writeToFile(p: Path, s: String)(implicit mat: Materializer): Unit = { Source.single(ByteString(s)).runWith(FileIO.toPath(p)) }
Akka docs > файл потокового ввода-вывода