Scala: записать строку в файл одним оператором

Для чтения файлов в Scala есть

Source.fromFile("file.txt").mkString

Есть ли эквивалентный и краткий способ записать строку в файл?

Большинство языков поддерживают что-то подобное. Мне больше всего нравится Groovy:

def f = new File("file.txt")
// Read
def s = f.text
// Write
f.text = "file contents"

Я хотел бы использовать код для программ от одной строки до короткой страницы кода. Использование собственной библиотеки здесь не имеет смысла. Я ожидаю, что современный язык позволит мне удобно записывать что-то в файл.

Есть сообщения, похожие на это, но они не отвечают на мой точный вопрос или сосредоточены на более старых версиях Scala.

Например:


person smartnut007    schedule 29.07.2011    source источник
comment
См. этот вопрос. Я согласен с наиболее оцененным ответом - лучше иметь свою личную библиотеку.   -  person ffriend    schedule 30.07.2011
comment
в этом случае я не согласен с тем, что нужно писать свою личную библиотеку. Обычно, когда вы пишете небольшие программы для выполнения специальных задач (возможно, я просто хочу написать ее как одностраничный скрипт scala или просто в REPL). Тогда доступ к личной библиотеке становится проблемой.   -  person smartnut007    schedule 31.07.2011
comment
В принципе, похоже, что на данный момент в scala 2.9 для этого нет ничего. Не знаю, как оставить этот вопрос открытым.   -  person smartnut007    schedule 01.08.2011
comment
Если вы выполните поиск java.io в исходном коде Scala, вы не найдете много вхождений, и еще меньше для операций вывода, особенно для PrintWriter. Итак, пока библиотека Scala-IO не станет официальной частью Scala, мы должны использовать чистую Java, как показано в парадигматике.   -  person PhiLho    schedule 29.08.2011
comment
да, вероятно, также нужен scala-io, совместимый с улучшениями ввода-вывода jdk7.   -  person smartnut007    schedule 29.08.2011
comment
@ smartnut007: А как насчет простого добавления вашей библиотеки в общесистемный путь к классам ($ CLASSPATH)?   -  person Blaisorblade    schedule 09.10.2011
comment
Почему бы не использовать утилиту Commons.io fileutils. записывать   -  person KIC    schedule 06.09.2017


Ответы (16)


Краткая одна строка:

import java.io.PrintWriter
new PrintWriter("filename") { write("file contents"); close }
person LRLucena    schedule 01.06.2015
comment
Хотя этот подход выглядит хорошо, он не безопасен ни в отношении исключений, ни в отношении кодирования. Если исключение происходит в write(), close никогда не будет вызываться, и файл не будет закрыт. PrintWriter также использует системную кодировку по умолчанию, что очень плохо для переносимости. И, наконец, этот подход генерирует отдельный класс специально для этой строки (однако, учитывая, что Scala уже генерирует тонны классов даже для простого кода, это вряд ли само по себе является недостатком). - person Vladimir Matveev; 18.08.2015
comment
Согласно приведенному выше комментарию, хотя это однострочный, это небезопасно. Если вы хотите большей безопасности, имея больше возможностей для определения местоположения и / или буферизации ввода, см. Ответ, который я только что опубликовал в аналогичной теме: stackoverflow .com / a / 34277491/501113 - person chaotic3quilibrium; 15.12.2015
comment
Для временного ведения журнала отладки я использовал new PrintWriter(tmpFile) { try {write(debugmsg)} finally {close} } - person Randall Whitman; 02.05.2017
comment
Стилистически, наверное, лучше использовать close(), я думаю - person Casey; 15.11.2017
comment
Это две строки, и их легко превратить в одну строку следующим образом: new java.io.PrintWriter("/tmp/hello") { write("file contents"); close } - person Philluminati; 03.04.2020

Странно, что никто не предлагал операции NIO.2 (доступны с Java 7):

import java.nio.file.{Paths, Files}
import java.nio.charset.StandardCharsets

Files.write(Paths.get("file.txt"), "file contents".getBytes(StandardCharsets.UTF_8))

Я думаю, что это, безусловно, самый простой, самый легкий и самый идиоматический способ, и он не требует никаких зависимостей, кроме самой Java.

person Vladimir Matveev    schedule 28.05.2014
comment
По какой-то причине это игнорирует символ новой строки. - person Kakaji; 12.03.2016
comment
@ Какадзи, не могли бы вы уточнить? Я только что протестировал его со строками с новой строкой, и он отлично работает. Он просто не может ничего фильтровать - Files.write () записывает массив байтов как blob, не анализируя его содержимое. В конце концов, в некоторых двоичных данных байт 0x0d может иметь важное значение, отличное от новой строки. - person Vladimir Matveev; 12.03.2016
comment
Дополнительное примечание: если у вас есть файл, просто «.toPath», чтобы получить первый параметр. - person akauppi; 11.05.2016
comment
Это более промышленный уровень (из-за явного выбора CharSets), но ему не хватает простоты (и одного лайнера ...) reflect.io.File.writeAll(contents). Почему? Три строки, когда вы включаете два оператора импорта. Возможно, IDE сделает это за вас автоматически, но в REPL это не так просто. - person WestCoastProjects; 25.01.2017
comment
@javadba, мы находимся в JVM, импорт практически не считается «строками», особенно потому, что альтернативой почти всегда является добавление новой зависимости библиотеки. В любом случае Files.write также принимает java.lang.Iterable в качестве второго аргумента, и мы можем получить его из Scala Iterable, то есть практически любого типа коллекции, используя конвертер: import java.nio.file.{Files, Paths}; import scala.collection.JavaConverters.asJavaIterableConverter; val output = List("1", "2", "3"); Files.write(Paths.get("output.txt"), output.asJava) - person Yawar; 21.05.2018

Вот краткий однострочный файл с использованием файла reflection.io, он работает в Scala 2.12:

reflect.io.File("filename").writeAll("hello world")

В качестве альтернативы, если вы хотите использовать библиотеки Java, вы можете сделать это:

Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
person Garrett Hall    schedule 11.02.2013
comment
+1 Отлично работает. Комбинация _1 _ / _ 2_ немного забавна, но она дает преимущество в виде отсутствия try / catch / finally. - person Brent Faust; 01.03.2013
comment
Что ж, если запись вызывает исключение, вы можете закрыть файл, если вы планируете восстановиться после исключения и снова прочитать / записать файл. К счастью, scala предоставляет однострочники и для этого. - person Garrett Hall; 01.03.2013
comment
Это не рекомендуется, поскольку пакет scala.tools.nsc.io не является частью общедоступного API, но используется компилятором. - person Giovanni Botta; 05.02.2014
comment
Взлом _1 _ / _ 2_ - это именно то, почему многие люди ненавидят Scala за нечитаемый код, который он заставляет создавать хакеры. - person Erik Kaplun; 17.02.2014
comment
Не работает со Scala 2.10.4. Они удаляют класс. - person Andreas Neumann; 26.05.2014
comment
Какая кодировка будет использоваться? - person rightfold; 10.09.2015
comment
scala.tootls.nsc.io.File - это псевдоним для reflect.io.File. По-прежнему внутренний API, но хоть немного короче. - person kostja; 17.09.2015
comment
Приведенное выше решение PrintWriter небезопасно; то есть p.close может не каждый вызываться, если p.write генерирует исключение. Кроме того, ваше решение не позволяет легко добавить BufferedWriter. Я обращаюсь к ним в решении, которое я задокументировал в другом аналогичном потоке: stackoverflow.com/a/34277491/501113 - person chaotic3quilibrium; 15.12.2015
comment
@kostja Спасибо за совет о reflect.io.File: теперь я могу заменить множество мест, которые я закодировал для scala.tools.nsc.io, на reflect.io, который более приемлем для Intellij и для обозревателей. - person WestCoastProjects; 25.01.2017

Если вам нравится синтаксис Groovy, вы можете использовать шаблон проектирования Pimp-My-Library, чтобы перенести его в Scala:

import java.io._
import scala.io._

class RichFile( file: File ) {

  def text = Source.fromFile( file )(Codec.UTF8).mkString

  def text_=( s: String ) {
    val out = new PrintWriter( file , "UTF-8")
    try{ out.print( s ) }
    finally{ out.close }
  }
}

object RichFile {

  implicit def enrichFile( file: File ) = new RichFile( file )

}

Это будет работать как положено:

scala> import RichFile.enrichFile
import RichFile.enrichFile

scala> val f = new File("/tmp/example.txt")
f: java.io.File = /tmp/example.txt

scala> f.text = "hello world"

scala> f.text
res1: String = 
"hello world
person paradigmatic    schedule 30.07.2011
comment
Вы никогда не вызываете close для возвращенного экземпляра Source.fromFile, что означает, что ресурс не закрывается до тех пор, пока он не будет GCed (сбор мусора). И ваш PrintWriter не буферизуется, поэтому он использует крошечный буфер JVM по умолчанию размером 8 байтов, что потенциально значительно замедляет ваш ввод-вывод. Я только что создал ответ в аналогичной ветке, посвященной этим проблемам: stackoverflow.com/a/34277491/501113 - person chaotic3quilibrium; 15.12.2015
comment
Ты прав. Но решение, которое я здесь дал, хорошо работает для недолговечных программ с небольшим вводом-выводом. Я не рекомендую его для серверных процессов или больших данных (как правило, более 500 МБ). - person paradigmatic; 15.12.2015

Я написал микробиблиотеку: https://github.com/pathikrit/better-files

file.write("Hi!")

or

file << "Hi!"
person pathikrit    schedule 14.09.2015
comment
Любите свою библиотеку! Этот вопрос является одним из самых популярных при поиске в Google того, как написать файл с помощью scala - теперь, когда ваш проект стал больше, вы, возможно, захотите немного расширить свой ответ? - person asachet; 06.06.2017

Вы можете легко использовать файловые утилиты Apache. Посмотрите на функцию writeStringToFile. Мы используем эту библиотеку в наших проектах.

person RyuuGan    schedule 09.02.2012
comment
Я тоже постоянно им пользуюсь. Если вы внимательно прочитали вопрос, я уже понял, почему я не хочу использовать библиотеку. - person smartnut007; 09.02.2012
comment
Без использования библиотеки я создал решение, которое обрабатывает исключения во время чтения / записи И может буферизоваться за пределами крошечных значений буфера по умолчанию, предоставляемых библиотеками Java: stackoverflow.com/a/34277491/501113 - person chaotic3quilibrium; 15.12.2015

У одного также есть этот формат, который является кратким, а основная библиотека красиво написана (см. Исходный код):

import scalax.io.Codec
import scalax.io.JavaConverters._

implicit val codec = Codec.UTF8

new java.io.File("hi-file.txt").asOutput.write("I'm a hi file! ... Really!")
person Dibbeke    schedule 04.09.2013

Думаю, это достаточно лаконично:

scala> import java.io._
import java.io._

scala> val w = new BufferedWriter(new FileWriter("output.txt"))
w: java.io.BufferedWriter = java.io.BufferedWriter@44ba4f

scala> w.write("Alice\r\nBob\r\nCharlie\r\n")

scala> w.close()
person Luigi Sgro    schedule 14.02.2014
comment
Достаточно честно, но это достаточно краткое не классифицируется как одно утверждение: P - person Erik Kaplun; 17.02.2014
comment
Этот код эптимизирует многие предполагаемые проблемы Java. К сожалению, Scala не считает ввод-вывод достаточно важным, поэтому стандартная библиотека его не включает. - person Chris; 25.08.2014
comment
Ответ скрывает проблемы с потерянными ресурсами с новым FileWriter. Если новый FileWriter завершается успешно, но новый BufferedWriter терпит неудачу, экземпляр FileWriter больше никогда не появляется и остается открытым до GCed (сбор мусора) и может не быть закрыт даже тогда (из-за того, как гарантии finalize работают в JVM). Я написал ответ на аналогичный вопрос, посвященный этим проблемам: stackoverflow.com/a/34277491/501113 - person chaotic3quilibrium; 15.12.2015

Вы можете сделать это с помощью сочетания библиотек Java и Scala. У вас будет полный контроль над кодировкой символов. Но, к сожалению, дескрипторы файлов закрываются некорректно.

scala> import java.io.ByteArrayInputStream
import java.io.ByteArrayInputStream

scala> import java.io.FileOutputStream
import java.io.FileOutputStream

scala> BasicIO.transferFully(new ByteArrayInputStream("test".getBytes("UTF-8")), new FileOutputStream("test.txt"))
person stefan.schwetschke    schedule 09.08.2013
comment
У вас есть проблема с экземпляром потерянного ресурса в вашем коде. Поскольку вы не захватываете экземпляры до своего вызова, если любой из них генерирует исключение до того, как вызываемому методу были переданы параметры, ресурсы, которые успешно созданы, не будут закрыты до тех пор, пока не будет выполнен сборщик мусора (сбор мусора), и даже тогда это может быть не из-за того, как GC гарантирует работу. Я написал ответ на аналогичный вопрос, посвященный этим проблемам: stackoverflow.com/a/34277491/501113 - person chaotic3quilibrium; 15.12.2015
comment
Вы правы, и ваше решение неплохое. Но здесь требовалось сделать это в одну строку. И если вы внимательно прочитаете, я упомянул утечку ресурсов в своем ответе как ограничение, связанное с этим требованием и моим подходом. Ваше решение хорошее, но не соответствует этому требованию одной строки. - person stefan.schwetschke; 15.12.2015

ОБНОВЛЕНИЕ: с тех пор я создал более эффективное решение, которое подробно изложил здесь: https://stackoverflow.com/a/34277491/501113

Я все больше и больше работаю над Scala Worksheet в Scala IDE для Eclipse (и я считаю, что в IntelliJ IDEA есть что-то эквивалентное). В любом случае, мне нужно сделать однострочный вывод для вывода некоторого содержимого, поскольку я получаю сообщение «Вывод превышает предел отсечения». сообщение, если я делаю что-нибудь существенное, особенно с коллекциями Scala.

Я придумал однострочник, который я вставляю в верхнюю часть каждого нового рабочего листа Scala, чтобы упростить это (и поэтому мне не нужно выполнять все упражнение по импорту внешней библиотеки для очень простой необходимости). Если вы приверженец и заметили, что технически это две строки, это только для того, чтобы сделать его более читабельным на этом форуме. Это одна строка в моем листе Scala.

def printToFile(content: String, location: String = "C:/Users/jtdoe/Desktop/WorkSheet.txt") =
  Some(new java.io.PrintWriter(location)).foreach{f => try{f.write(content)}finally{f.close}}

И использование просто:

printToFile("A fancy test string\ncontaining newlines\nOMG!\n")

Это позволяет мне при желании указать имя файла, если я хочу иметь дополнительные файлы сверх значения по умолчанию (которое полностью перезаписывает файл при каждом вызове метода).

Итак, второе использование просто:

printToFile("A fancy test string\ncontaining newlines\nOMG!\n", "C:/Users/jtdoe/Desktop/WorkSheet.txt")

Наслаждаться!

person chaotic3quilibrium    schedule 31.10.2013

Я знаю, что это не одна строка, но, насколько я могу судить, она решает проблемы безопасности;

// This possibly creates a FileWriter, but maybe not
val writer = Try(new FileWriter(new File("filename")))
// If it did, we use it to write the data and return it again
// If it didn't we skip the map and print the exception and return the original, just in-case it was actually .write() that failed
// Then we close the file
writer.map(w => {w.write("data"); w}).recoverWith{case e => {e.printStackTrace(); writer}}.map(_.close)

Если вас не заботила обработка исключений, вы можете написать

writer.map(w => {w.writer("data"); w}).recoverWith{case _ => writer}.map(_.close)
person J_mie6    schedule 06.01.2017

Используйте библиотеку аммонитовые операции. Синтаксис очень минимален, но ширина библиотеки почти такая же, как и следовало ожидать от попытки выполнения такой задачи на языке сценариев оболочки, таком как bash.

На странице, на которую я указал, показаны многочисленные операции, которые можно выполнять с библиотекой, но чтобы ответить на этот вопрос, это пример записи в файл.

import ammonite.ops._
write(pwd/'"file.txt", "file contents")
person smac89    schedule 27.01.2020
comment
Это сработало только у меня: write(pwd/"version", version) нет ' - person pme; 02.09.2020

Вот современный и безопасный лайнер:

java.nio.file.Files.write(java.nio.file.Paths.get("/tmp/output.txt"), "Hello world".getBytes());

nio - это современная библиотека ввода-вывода, поставляемая по умолчанию с JDK 9+, поэтому импорт или зависимости не требуются.

person Philluminati    schedule 23.02.2021

os-lib - лучший современный способ записи в файл, как указано здесь.

Вот как написать привет file.txt файлу.

os.write(os.pwd/"file.txt", "hello")

os-lib скрывает уродство и сложность Java (он использует библиотеки Java под капотом, поэтому он такой же производительный). Дополнительную информацию об использовании библиотеки см. здесь. .

person Powers    schedule 14.12.2020

Благодаря магии точки с запятой вы можете сделать все, что угодно, однострочным.

import java.io.PrintWriter
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.charset.StandardCharsets
import java.nio.file.StandardOpenOption
val outfile = java.io.File.createTempFile("", "").getPath
val outstream = new PrintWriter(Files.newBufferedWriter(Paths.get(outfile)
  , StandardCharsets.UTF_8
  , StandardOpenOption.WRITE)); outstream.println("content"); outstream.flush(); outstream.close()
person Ion Freeman    schedule 16.02.2017
comment
Здесь нет аргументов. Я решил не вспоминать, какие API нужны мне, чтобы очистить часть моей жизни, поэтому я просто всегда это делаю. - person Ion Freeman; 26.05.2018

person    schedule
comment
У меня не работает REPL. Нет сообщения об ошибке, но если я посмотрю на /tmp/example.txt, его нет. - person user unknown; 09.02.2012
comment
@user unknown, извините за отсутствие символа '!' в конце команды, теперь исправлено. - person xiefei; 09.02.2012
comment
Чудесно! Теперь, когда это работает, я хотел бы знать, почему. Что такое #>, что делает !? - person user unknown; 09.02.2012
comment
Это решение не переносимо! работает только в системах * nix. - person Giovanni Botta; 17.01.2014
comment
Это не сработает для записи произвольных строк. Это будет работать только для коротких строк, которые вы можете передать в качестве аргументов в echo инструмент командной строки. - person Rich; 11.04.2014