Як записати у файл у Scala?


157

Для читання є корисна абстракція Source. Як я можу записати рядки до текстового файлу?


1
Якщо ви знаєте, як це зробити на Java, то ви можете використовувати те саме в Scala. Ваше питання конкретно щодо стандартної бібліотеки Scala?
пшениці

1
@wheaties так найкращий спосіб зробити це в scala
yura

Ця бібліотека справді гарна: github.com/pathikrit/better-files
Робін

Відповіді:


71

Редагуйте 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)
  }

15
А як щодо версії Scala 2.9? :)
Едуардо Коста

Проект масштабування здається мертвим (жодних зобов'язань з червня 2009 року). Чи це правильно? Скалакс фіксує історію
Rick-777

@Eduardo: Я доповнив свою відповідь новим місцем для бібліотеки scala-io (яке було оновлено для Scala2.9: github.com/jesseeichar/scala-io/isissue/20 )
VonC,

10
Це дійсно поточна пропозиція для Scala 2.10? Використовувати Scala IO? Ще немає нічого в основному Scala?
Філ

2
Я ніколи не використовував scalax.io, але судячи з цих прикладних рядків, здається, що його дизайн API досить поганий. Змішування методів символів та двійкових даних в одному інтерфейсі мало сенсу і дуже ймовірно призведе до кодування помилок, які важко знайти. Дизайн java.io (Reader / Writer vs. 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
новий java.io.PrintWriter () використовує кодування платформи за замовчуванням, що, ймовірно, означає, що файл результату не дуже портативний. Наприклад, якщо ви хочете створити файл, який ви зможете пізніше надіслати електронною поштою, вам, мабуть, слід скористатися конструктором PrintWriter, який дозволяє вказати кодування.
jcsahnwaldt Відновити Моніку

@JonaChristopherSahnwaldt - Звичайно, в особливих випадках ви можете вказати кодування. Типовий для платформи - це найрозумніший дефолт в середньому. Те саме, що і з Source(кодування за замовчуванням за замовчуванням). Звичайно, ви можете додати, наприклад, enc: Option[String] = Noneпараметр після, fякщо вважаєте це загальною потребою.
Рекс Керр

6
@RexKerr - я не згоден. Слід вказати кодування майже у всіх випадках. Більшість помилок кодування, з якими я стикаюся, трапляються через те, що люди не розуміють чи не думають про кодування. Вони використовують за замовчуванням і навіть не знають цього, оскільки занадто багато API дозволяють їм уникнути цього. Сьогодні, найбільш розумним за замовчуванням, мабуть, буде UTF-8. Можливо, ви працюєте лише з англійською та іншими мовами, які можна записати в ASCII. Щасливчик. Я живу в Німеччині і мені довелося виправляти більше розбитих умлаутів, ніж я дбайливо пам'ятаю.
jcsahnwaldt Відновити Моніку

3
@JonaChristopherSahnwaldt - Це привід мати розумне кодування за замовчуванням, а не змушувати всіх його постійно вказувати. Але якщо ви на Mac, а ваші файли, написані Java, є gobbledygook, оскільки вони не кодовані Mac OS Роман, я не впевнений, що це приносить більше користі, ніж шкоди. Я думаю, що вини платформ у тому, що вони не домовилися про сукупність. Як індивідуальний розробник, введення рядка насправді не вирішить проблему. (Усі розробники погоджуються на UTF-8, але тоді це може просто зайти за замовчуванням.)
Rex Kerr

@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)
    }
  }

тощо.


39
Не зрозумійте мене неправильно, мені подобається ваш код, і він дуже навчальний, але чим більше я бачу такі конструкції для простих проблем, тим більше це нагадує мені про старий жарт "привіт": ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-) (+1 голос від мене).
greenoldman

4
Якщо ви пишете однолінійки, взагалі нічого не має значення. Якщо ви пишете значні програми (великі з постійною потребою у обслуговуванні та розвитку), такий спосіб мислення призводить до найшвидшого та згубного погіршення якості програмного забезпечення.
Рандалл Шульц

3
Не у всіх будуть "очі очі" до певного рівня практики - смішно бачити, що цей приклад коду йде з "Починаючи" Scala
asyncwait

asyncwait "на початку" скала ... найронічніша назва будь-коли, зверніть увагу: у мене є книга ... і я просто зараз починаю її розуміти. Я вважаю, чим я був крок до "початківця" хаха: D ........
користувач1050817

1
Проблема полягає в менших хитрощах Scala, а в багатослівності та поганому стилі. Я відредагував це, щоб набагато читати. Після мого рефактора це лише 4 лінії (ну, 4 з довжиною ліній IDE, тут використовується 6 для розміщення на екрані). ІМХО, це дуже приємна відповідь.
samthebest

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()
  }

1
@samthebest ви могли б додати бібліотеки, з яких ви import?
Даніель

1
Станом на java 7, використовуйте замість java.nio.file: def writeToFile (файл: String, stringToWrite: String): Unit = {val write = Files.newBufferedWriter (Paths.get (файл)), спробуйте списати.write (stringToWrite) нарешті author.close ()}
E Shindler

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


2
Вибачте, але ваш код можна уявити, він не відповідає implementedнеобхідній умові. Ви не можете використовувати код, який не реалізований. Я маю на увазі, що ви повинні сказати, як його знайти, оскільки він за замовчуванням не доступний і не відомий.
Валь,

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)
Brent Faust

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


Чому це не підходить для великих файлів?
Chetan Bhasin

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

Він має один вкладиш для запису у файли , додавання до файлів , перезапис файлів та багато інших корисних / звичайних операцій


ДЯКУЮ за це оновлення. Отримано. Я посилався на вашу відповідь у власному вище для більшої наочності.
VonC

7

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

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

або

file << "Hello" << "\n" << "World"

Ось також - Це питання є одним із найкращих моментів у Google, як написати файл зі скалою - тепер, коли ваш проект посилився, ви можете трохи розширити свою відповідь?
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якщо finallyкод кинувException

Переглянувши всі ці відповіді про те, як легко написати файл у 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і запишемо 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.AutoCloseablescala.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 швидше перебирати цей конкретний горб.


2
Неймовірне оновлення. Єдина проблема полягає в тому, що зараз вам подобається 100 рядків коду, які можна замінити try-catch-finally. Ще любите свою пристрасть.
Спостерігач

1
@Observer Я б запевнив, що це неточне твердження. Я описую схему - це фактично зменшення кількості шаблону котла, який повинен записувати клієнт, щоб забезпечити належне поводження із закриттям автозахисних блоків, одночасно ввімкнувши ідіоматичну схему FP Scala за допомогою scala.util.Try. Якщо ви намагаєтесь досягти тих самих ефектів, які я маю, вручну виписуючи блоки try / catch / нарешті, я думаю, ви знайдете, що ви отримаєте трохи більше котла, ніж ви собі уявляли. Отже, є важливе значення читабельності, коли підштовхувати всю котельну плиту до 100 ліній функції Scala.
хаотична рівновага

1
Вибачте, якщо це звучало образливо. І все-таки я можу сказати, що немає необхідності в такому обсязі коду, тому що цього можна досягти за допомогою нефункціонального підходу з набагато більшою простотою. Особисто я б написав спробу, нарешті, з деякими додатковими чеками. Це просто коротше. Якщо я хотів використовувати обгортки, ApacheUtils є там, щоб використати всю брудну роботу. Більше того, всі стандартні програми Reader / Writer мають близькі базові потоки, тому багатоповерхове обговорення не потрібно. PS: Я змінив свій голос з мінус один на плюс один, щоб підтримати ваші зусилля. Тож, будь ласка, не підозрюй мене в поганих намірах.
Спостерігач

Не вчинено правопорушень.
хаотична рівновага

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

Для цього використовується "тик качки", який залежить від рефлексії. Для багатьох контекстів залежність від рефлексії не є початковою.
хаотичний3рівноваги

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 }

0

Якщо у вас все-таки є проект Akka Streams у вашому проекті, він пропонує одношарову лінію:

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

Документи Akka> IO потокового файлу

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.