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

110

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

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

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

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

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

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

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

Например:

  • 0
    Смотрите этот вопрос. Я согласен с самым высоким рейтингом ответа - лучше иметь свою личную библиотеку.
  • 1
    в этом случае я не согласен с тем, что нужно писать свою личную библиотеку. Обычно, когда вы пишете небольшие программы для специальных действий (может быть, я просто хочу написать это как одностраничный скрипт scala или просто в REPL). Тогда доступ к личной библиотеке становится болью.
Показать ещё 5 комментариев
Теги:
scala-2.9

13 ответов

44
Лучший ответ

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

new PrintWriter("filename") { write("file contents"); close }
  • 12
    Хотя этот подход выглядит хорошо, он не является ни безопасным для исключений, ни безопасным для кодирования. Если в write() происходит исключение, close никогда не будет вызываться, и файл не будет закрыт. PrintWriter также использует системную кодировку по умолчанию, что очень плохо для переносимости. И наконец, этот подход генерирует отдельный класс специально для этой строки (однако, учитывая, что Scala уже генерирует тонны классов даже для простого кода, сам по себе это вряд ли является недостатком).
  • 0
    Согласно приведенному выше комментарию, хотя это один вкладыш, это небезопасно. Если вы хотите большей безопасности, имея больше возможностей для определения местоположения и / или буферизации ввода, посмотрите ответ, который я только что опубликовал в аналогичной теме: stackoverflow.com/a/34277491/501113
Показать ещё 2 комментария
111

Странно, что никто не предлагал операции 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.

  • 0
    Это игнорирует символ новой строки по какой-то причине.
  • 0
    @ Какаджи, не могли бы вы уточнить? Я только что проверил его со строками с символами новой строки, и он отлично работает. Он просто не может ничего фильтровать - Files.write () записывает байтовый массив в виде большого двоичного объекта, не анализируя его содержимое. В конце концов, в некоторых двоичных данных 0x0d байт может иметь важное значение, кроме новой строки.
Показать ещё 3 комментария
79

Ниже приведен краткий однострочный файл с использованием библиотеки компилятора Scala:

scala.tools.nsc.io.File("filename").writeAll("hello world")

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

Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
  • 1
    +1 Прекрасно работает. Комбинация Some / foreach немного прикольная, но в ней нет преимущества try / catch / finally.
  • 3
    Хорошо, если запись выдает исключение, вы можете закрыть файл, если вы планируете восстановление из исключения и повторное чтение / запись файла. К счастью, в Scala предусмотрены однострочники.
Показать ещё 9 комментариев
38

Если вам нравится синтаксис 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
  • 1
    Вы никогда не вызываете close для возвращенного экземпляра Source.fromFile, что означает, что ресурс не закрывается, пока он не будет собран GCed (сборщик мусора). И ваш PrintWriter не буферизуется, поэтому он использует крошечный буфер JVM по умолчанию в 8 байт, что потенциально значительно замедляет ваш ввод-вывод. Я только что создал ответ на аналогичную тему, которая занимается этими проблемами: stackoverflow.com/a/34277491/501113
  • 1
    Вы правы. Но решение, которое я здесь дал, хорошо работает для краткосрочных программ с небольшим вводом-выводом. Я не рекомендую его для серверных процессов или больших данных (как правило, более 500 МБ).
20
import sys.process._
"echo hello world" #> new java.io.File("/tmp/example.txt") !
  • 0
    Это не работает для меня в REPL. Нет сообщения об ошибке, но если я смотрю на /tmp/example.txt, то нет.
  • 0
    @ пользователь неизвестен, извините за пропущенный '!' в конце команды, исправлено сейчас.
Показать ещё 4 комментария
11

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

  • 3
    Я использую это все время тоже. Если вы внимательно прочитаете вопрос, я уже понял, почему я не хочу использовать библиотеку.
  • 0
    Не используя библиотеку, я создал решение, которое обрабатывает исключения во время чтения / записи и может буферизоваться за пределами крошечных настроек буфера по умолчанию, предоставляемых библиотеками Java: stackoverflow.com/a/34277491/501113
7

Микробиблиотека, которую я написал: https://github.com/pathikrit/better-files

file.write("Hi!")

или

file << "Hi!"
  • 2
    Люблю свою библиотеку! Этот вопрос является одним из самых популярных при поиске, как написать файл с scala - теперь, когда ваш проект стал больше, возможно, вы захотите немного расширить свой ответ?
6

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

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!")
5

Это достаточно кратким, я думаю:

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()
  • 4
    Справедливо, но это «достаточно кратко» не классифицируется как «одно утверждение»: P
  • 0
    Этот код олицетворяет многие из предполагаемых проблем Java. К сожалению, Scala не считает IO достаточно важным, поэтому стандартная библиотека не включает его.
Показать ещё 1 комментарий
2

Вы можете сделать это с помощью сочетания 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"))
  • 0
    В вашем коде есть проблема с потерянным экземпляром ресурса. Так как вы не захватываете экземпляры до вашего вызова, если любой из них генерирует исключение до того, как вызываемый метод передал параметры, ресурсы, которые успешно созданы, не будут закрываться до GCed (Garbage Collected), и даже тогда не может быть из-за того, как GC гарантирует работу. Я написал ответ на аналогичный вопрос, который решает эти проблемы: stackoverflow.com/a/34277491/501113
  • 1
    Вы правы, и ваше решение довольно хорошее. Но здесь требовалось сделать это в одну строку. И когда вы внимательно читаете, я упомянул утечку ресурсов в своем ответе как ограничение, которое сопровождает это требование и мой подход. Ваше решение хорошо, но не соответствует этому требованию одной строки.
1

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

// 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)
1

ОБНОВЛЕНИЕ: с тех пор я создал более эффективное решение, на котором я подробно остановился здесь: https://stackoverflow.com/questions/4604237/how-to-write-to-a-file-in-scala

Я все больше и больше работаю в Scala Рабочем листе в 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")

Наслаждайтесь!

0

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

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()
  • 0
    нет необходимости промывать перед закрытием.
  • 0
    Здесь нет аргументов. Я решил не вспоминать, какие API нужны мне, чтобы очистить часть моей жизни, поэтому я всегда делаю это.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню