Как записать в файл в Scala?

157

Для чтения есть полезная абстракция Source. Как я могу записать строки в текстовый файл?

юра
источник
1
Если вы знаете, как это сделать в Java, то вы можете использовать то же самое в Scala. Ваш вопрос относится к стандартной библиотеке Scala?
Wheaties
1
@wheaties да лучший способ сделать это в Scala
Юра
Эта библиотека действительно хороша: github.com/pathikrit/better-files
Робин
Библиотека OS-Lib от
Lihaoyi

Ответы:

71

Править 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)
  }
VonC
источник
15
А как насчет версии Scala 2.9? :)
Эдуардо Коста
Проект scalax кажется мертвым (никаких коммитов с июня 2009 года). Это правильно? История коммитов скалякса
Rick-777
@Eduardo: я завершил свой ответ новым местом для библиотеки scala-io (которое было обновлено для Scala2.9: github.com/jesseeichar/scala-io/issues/20 )
VonC
10
Это действительно актуальное предложение для Scala 2.10? Использовать Scala IO? В ядре Scala еще ничего нет?
Фил
2
Я никогда не использовал scalax.io, но, судя по этим примерам, кажется, что его API-интерфейс довольно плохой. Смешивание методов для символьных и двоичных данных в одном интерфейсе не имеет большого смысла и, скорее всего, приведет к ошибкам кодирования, которые трудно найти. Дизайн java.io (Reader / Writer против InputStream / OutputStream) выглядит намного лучше.
jcsahnwaldt восстановить Монику
211

Это одна из функций, отсутствующих в стандартной версии 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)
}
Рекс Керр
источник
1
new java.io.PrintWriter () использует кодировку платформы по умолчанию, что, вероятно, означает, что файл результатов не очень переносим. Например, если вы хотите создать файл, который позже можете отправить по электронной почте, вам, вероятно, следует использовать конструктор PrintWriter, который позволяет указать кодировку.
jcsahnwaldt Восстановить Монику
@JonaChristopherSahnwaldt - Конечно, в особых случаях вы можете указать кодировку. Значение по умолчанию для платформы - это наиболее разумное значение по умолчанию в среднем. То же, что и с Source(кодировка по умолчанию по умолчанию). Вы, конечно, можете добавить, например, enc: Option[String] = Noneпараметр после, fесли вы считаете это общей потребностью.
Рекс Керр
6
@RexKerr - я не согласен. Следует указывать кодировку практически во всех случаях. Большинство ошибок кодирования, с которыми я сталкиваюсь, происходит потому, что люди не понимают или не думают о кодировании. Они используют значение по умолчанию и даже не знают его, потому что слишком много API позволяют им сойти с рук. В настоящее время наиболее разумным значением по умолчанию, вероятно, будет UTF-8. Возможно, вы работаете только с английским и другими языками, которые могут быть написаны в ASCII. Повезло тебе. Я живу в Германии, и мне пришлось починить больше сломанных умлаутов, чем я хочу вспомнить.
jcsahnwaldt Восстановить Монику
3
@JonaChristopherSahnwaldt - это причина иметь разумную кодировку по умолчанию, а не заставлять всех указывать ее постоянно. Но если вы работаете на Mac, а ваши файлы, написанные на Java, являются пустяковыми, потому что они не закодированы в Mac OS Roman, я не уверен, что это приносит больше пользы, чем вреда. Я думаю, это вина платформ, что они не договорились о кодировке. Как индивидуальный разработчик, ввод в строку действительно не решит проблему. (Все разработчики соглашаясь на UTF-8 будет, но тогда это может просто пойти по умолчанию.)
Rex Керр
@JonaChristopherSahnwaldt +10 за исправление всего сломанного умлаута. Не можете использовать молоток, возможно, дырокол? Или это уже дыры, которые нуждаются в заполнении, может быть, этот парень может помочь youtube.com/watch?v=E-eBBzWEpwE Но если серьезно, то влияние ASCII так пагубно в мире, согласитесь, его следует указать, а значение по умолчанию как UTF- 8
Давос,
50

Похожий на ответ Рекса Керра, но более общий. Сначала я использую вспомогательную функцию:

/**
 * 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)
    }
  }

и т.п.

Jus12
источник
39
Не поймите меня неправильно, мне нравится ваш код, и он очень познавательный, но чем больше я вижу такие конструкции для простых задач, тем больше он напоминает мне о старой шутке « Здравствуй, мир»: ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-) (+1 голос от меня).
Гринольдман
4
Если ты пишешь одну строчку, ничего не имеет значения. Если вы пишете важные программы (большие с постоянной потребностью в обслуживании и развитии), этот тип мышления приводит к наиболее быстрому и пагубному ухудшению качества программного обеспечения.
Рэндалл Шульц
3
Не у всех будут "глаза скалы" до некоторого уровня практики - забавно видеть, что этот пример кода прибывает из "Начинающего" Scala
asyncwait
asyncwait "начало" scala ... самое ироничное название когда-либо, обратите внимание: у меня есть книга ... и только сейчас я начинаю понимать это .. Я полагаю, чем я был на шаг раньше, чем "новичок" LOL: D ........
user1050817
1
Проблема здесь не в трюках Scala, а в многословности и плохом стиле. Я отредактировал это, чтобы сделать его более читабельным. После моего рефакторинга это всего 4 строки (ну, 4 с длиной строки IDE, здесь 6 используется, чтобы поместиться на экране). ИМХО, сейчас очень хороший ответ.
Самбест
38

Простой ответ:

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()
  }
samthebest
источник
1
@samthebest не могли бы вы добавить библиотеки importиз?
Даниэль
1
Начиная с Java 7, используйте вместо этого java.nio.file: def writeToFile (file: String, stringToWrite: String): Unit = {val writer = Files.newBufferedWriter (Paths.get (file)), в конце концов, попробуйте writeer.write (stringToWrite) writer.close ()}
Э Шиндлер
20

Дать другой ответ, потому что мои правки других ответов были отклонены.

Это самый краткий и простой ответ (похожий на ответ Гаррета Холла)

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, ни лямбды, а обратите внимание на использование синтаксиса заполнителя. Также обратите внимание на лучшее именование.

samthebest
источник
2
Извините, но ваш код мыслимый, он не выполняет implementedпредварительных условий. Вы не можете использовать код, который не реализован. Я имею в виду, что вы должны сказать, как его найти, так как он не доступен по умолчанию и малоизвестен.
Val
15

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

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

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

Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
Гарретт Холл
источник
Какой импорт? то есть откуда приходит файл?
Бен Хатчисон
Библиотека компилятора Scala.
Гаррет Холл
3
Больше не жизнеспособен (не в Scala 2.11)
Брент Фауст
1
О чем ты говоришь? scala.tools.nsc.io.File("/tmp/myFile.txt")работает в Scala 2.11.8.
1
Теперь он в scala.reflect.io.File
Кит Нордстрем,
13

Один лайнер для сохранения / чтения в / из 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

Ник Залуцкий
источник
Почему это не подходит для больших файлов?
Четан Бхасин
2
@ChetanBhasin Вероятно, потому что writeбудет копировать contentsв новый байтовый массив вместо потоковой передачи его в файл, таким образом, на пике он использует вдвое больше памяти, чем contentsодин.
Даниэль Вернер
10

К сожалению для лучшего ответа, 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")

Он имеет одну строку для записи в файлы , добавления к файлам , перезаписи файлов и многих других полезных / общих операций

Ли Хаои
источник
Спасибо за это обновление. Upvoted. Я сослался на ваш ответ выше для большей наглядности.
VonC
7

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

file.appendLine("Hello", "World")

или

file << "Hello" << "\n" << "World"
pathikrit
источник
И здесь - этот вопрос является одним из самых популярных при поиске, как написать файл с scala - теперь, когда ваш проект стал больше, возможно, вы захотите немного расширить свой ответ?
ASAC
6

Начиная Scala 2.13, стандартная библиотека обеспечивает специализированную утилиту управления ресурсами: Using.

В этом случае его можно использовать с такими ресурсами, как PrintWriterили BufferedWriterрасширяющимися AutoCloseableдля записи в файл и, несмотря ни на что, после этого закройте ресурс:

  • Например, с java.ioAPI:

    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.nioAPI:

    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"))
    }
Ксавье Гихот
источник
6

ОБНОВЛЕНИЕ 2019 / сентябрь / 01:

  • Начиная с Scala 2.13, предпочитайте использовать scala.util.Using
  • Исправлена ​​ошибка, из-за которой finallyпроглатывался оригинальный Exceptionброшенный код, tryеслиfinallyException

Изучив все эти ответы о том, как легко написать файл в Scala, и некоторые из них довольно хороши, у меня возникло три вопроса:

  1. В ответе Jus12 , использование каррирования для использования вспомогательного метода не очевидно для начинающих Scala / FP
  2. Необходимо инкапсулировать ошибки более низкого уровня с scala.util.Try
  3. Требуется показать 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 => {
        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 быстрее справиться с этой проблемой.

chaotic3quilibrium
источник
2
Невероятное обновление. Единственная проблема в том, что теперь у вас есть около 100 строк кода, которые можно заменить на try-catch-finally. Все еще любите свою страсть.
наблюдатель
1
@ Observer Я бы сказал, что это неточное утверждение. Шаблон, который я описываю, на самом деле уменьшает количество шаблонов, которые клиент должен написать, чтобы обеспечить правильную обработку закрытия AutoCloseables, а также включить идиоматический шаблон Scala FP с использованием scala.util.Try. Если вы попытаетесь добиться того же эффекта, что и я, вручную выписав блоки try / catch / finally, я думаю, вы обнаружите, что в итоге вы получите немного больше стандартного образца, чем вы себе представляете. Таким образом, существует значительная читаемость при вставке всего шаблона в 100 строк функции Scala.
chaotic3quilibrium
1
Извините, если это звучит оскорбительно. Тем не менее, я хочу сказать, что нет необходимости в таком количестве кода, потому что этого можно достичь с помощью нефункционального подхода с гораздо большей простотой. Лично я бы написал try-finally с некоторыми дополнительными проверками. Это просто короче. Если бы я хотел использовать обертки, ApacheUtils должен использовать всю грязную работу. Более того, все стандартные устройства чтения / записи закрывают основные потоки, поэтому многопоточность не требуется. PS: я изменил свой голос с минус одного на плюс один, чтобы поддержать ваши усилия. Поэтому, пожалуйста, не подозревайте меня в плохих намерениях.
наблюдатель
Без обид.
chaotic3quilibrium
1
Я понимаю вашу точку зрения. Спасибо за обсуждение, я должен немного подумать об этом. Хорошего дня!
наблюдатель
3

Вот пример записи некоторых строк в файл с использованием 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
Крис Мартин
источник
1

Чтобы превзойти 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))
Epicurist
источник
Это использует "типизацию утки", которая зависит от отражения. Для многих контекстов зависимость от отражения не является началом.
chaotic3quilibrium
1

Нет зависимостей, с обработкой ошибок

  • Использует методы только из стандартной библиотеки
  • Создает каталоги для файла, если необходимо
  • Использует 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")
}
Матиас Браун
источник
1

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()
}
  1. Импорт библиотек Java: IO и NIO
  2. Создать Pathобъект с выбранным вами именем файла
  3. Преобразуйте ваш текст, который вы хотите вставить в файл, в байтовый массив
  4. Получите ваш файл в виде потока: OutputStream
  5. Передайте ваш байтовый массив в writeфункцию вашего выходного потока
  6. Закрыть поток
Янак Мина
источник
1

Аналогично этому ответу , вот пример с 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)

  }
}
Вали Диа
источник
0

Эта строка помогает записать файл из массива или строки.

 new PrintWriter(outputPath) { write(ArrayName.mkString("")); close }
Vickyster
источник
0

Если у вас в любом случае есть Akka Streams в вашем проекте, он предоставляет одну строку:

def writeToFile(p: Path, s: String)(implicit mat: Materializer): Unit = {
  Source.single(ByteString(s)).runWith(FileIO.toPath(p))
}

Akka docs> Потоковый файл IO

akauppi
источник