Como gravar em um arquivo no Scala?

157

Para leitura, existe a abstração útil Source. Como escrever linhas em um arquivo de texto?

yura
fonte
1
Se você souber fazer isso em Java, poderá usar o mesmo no Scala. Sua pergunta está especificamente na biblioteca padrão do Scala?
wheaties
1
@wheaties sim melhor maneira de fazer isso em scala
Yura
Esta biblioteca é realmente boa: github.com/pathikrit/better-files
Robin
Biblioteca OS-Lib de Lihaoyi github.com/lihaoyi/os-lib
WeiChing

Respostas:

71

Edit 2019 (8 years later), Scala-IO não sendo muito ativo, se houver, Li Haoyi sugere sua própria biblioteca lihaoyi/os-lib, que ele apresenta abaixo .

Em junho de 2019, Xavier Guihot menciona em sua resposta a biblioteca Using, um utilitário para executar o gerenciamento automático de recursos.


Editar (setembro de 2011): desde que Eduardo Costa pergunta sobre o Scala2.9, e desde que Rick-777 comenta que o scalax.IO confirma o histórico praticamente inexistente desde meados de 2009 ...

O Scala-IO mudou de lugar: veja seu repositório GitHub , de Jesse Eichar (também no SO ):

O projeto guarda-chuva Scala IO consiste em alguns subprojetos para diferentes aspectos e extensões de IO.
Existem dois componentes principais do Scala IO:

  • Núcleo - O núcleo lida principalmente com a leitura e gravação de dados de e para fontes e sumidouros arbitrários. Os traços canto de pedra são Input, Outpute Seekableque fornecem a API núcleo.
    Outras classes de importância são Resource, ReadCharse WriteChars.
  • File - File é uma API File(chamada Path) que é baseada em uma combinação do sistema de arquivos Java 7 NIO e APIs do SBT PathFinder.
    Pathe FileSystemsão os principais pontos de entrada na API do Scala IO File.
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)

Resposta original (janeiro de 2011), com o antigo local para scala-io:

Se você não quiser esperar pelo Scala2.9, poderá usar a biblioteca scala-incubator / scala-io .
(como mencionado em " Por que o Scala Source não fecha o InputStream subjacente? ")

Veja as amostras

{ // 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
fonte
15
Que tal uma versão Scala 2.9? :)
Eduardo Costa
O projeto scalax parece morto (nenhum commit desde junho de 2009). Isto está certo? histórico de consolidação de scalax
Rick-777
@ Eduardo: Eu completei minha resposta com o novo local da biblioteca scala-io (que foi atualizado para o Scala2.9: github.com/jesseeichar/scala-io/issues/20 )
VonC
10
Essa é realmente a sugestão atual para o Scala 2.10? Usar Scala IO? Ainda não há nada no núcleo do Scala?
Phil
2
Eu nunca usei o scalax.io, mas, a partir dessas linhas de exemplo, parece que seu design de API é muito ruim. A combinação de métodos para caracteres e dados binários em uma interface faz pouco sentido e provavelmente levará a erros de codificação difíceis de encontrar. O design do java.io (Reader / Writer vs. InputStream / OutputStream) parece muito melhor.
Jcsahnwaldt Restabelecer Monica
211

Esse é um dos recursos ausentes do Scala padrão que achei tão útil que o adicionei à minha biblioteca pessoal. (Você provavelmente também deve ter uma biblioteca pessoal.) O código é assim:

def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) {
  val p = new java.io.PrintWriter(f)
  try { op(p) } finally { p.close() }
}

e é usado assim:

import java.io._
val data = Array("Five","strings","in","a","file!")
printToFile(new File("example.txt")) { p =>
  data.foreach(p.println)
}
Rex Kerr
fonte
1
O novo java.io.PrintWriter () usa a codificação padrão da plataforma, o que provavelmente significa que o arquivo de resultado não é muito portátil. Por exemplo, se você quiser produzir um arquivo que possa ser enviado posteriormente por e-mail, provavelmente deverá usar o construtor PrintWriter que permite especificar uma codificação.
jcsahnwaldt Restabelece Monica
@JonaChristopherSahnwaldt - Claro, em casos especiais, você pode querer especificar a codificação. O padrão para a plataforma é o padrão mais sensato, em média. O mesmo que com Source(codificação padrão por padrão). Obviamente, você pode adicionar, por exemplo, um enc: Option[String] = Noneparâmetro depois fse achar isso uma necessidade comum.
Rex Kerr
6
@RexKerr - eu discordo. Deve-se especificar a codificação em quase todos os casos. A maioria dos erros de codificação que encontro acontecem porque as pessoas não entendem ou não pensam em codificação. Eles usam o padrão e nem sabem disso, porque muitas APIs permitem que eles se safem. Atualmente, o padrão mais sensato provavelmente seria o UTF-8. Talvez você trabalhe apenas com inglês e outros idiomas que possam ser escritos em ASCII. Sortudo. Eu moro na Alemanha e tive que consertar mais tremas quebrados do que gostaria de lembrar.
Jcsahnwaldt Restabelecer Monica
3
@JonaChristopherSahnwaldt - Esse é um motivo para ter uma codificação padrão sensata, para não forçar todos a especificá-la o tempo todo. Mas se você estiver em um Mac e seus arquivos escritos por Java forem muito engraçados porque não são codificados em Mac OS Roman, não tenho certeza se está fazendo mais bem do que mal. Eu acho que é culpa das plataformas que eles não concordaram com um conjunto de caracteres. Como desenvolvedor individual, digitar uma string realmente não resolverá o problema. (Todos os desenvolvedores concordando em UTF-8 seria, mas, em seguida, que pode simplesmente ir em como o padrão.)
Rex Kerr
@JonaChristopherSahnwaldt +10 por corrigir todo o trema quebrado. Não pode usar um martelo, talvez um furador? Ou eles já são buracos que precisam ser preenchidos, talvez esse cara possa ajudar youtube.com/watch?v=E-eBBzWEpwE Mas, sério, a influência do ASCII é tão prejudicial no mundo, concorda que deve ser especificado e usar como padrão UTF- 8
Davos
50

Semelhante à resposta de Rex Kerr, mas mais genérica. Primeiro eu uso uma função auxiliar:

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

Então eu uso isso como:

def writeToFile(fileName:String, data:String) = 
  using (new FileWriter(fileName)) {
    fileWriter => fileWriter.write(data)
  }

e

def appendToFile(fileName:String, textData:String) =
  using (new FileWriter(fileName, true)){ 
    fileWriter => using (new PrintWriter(fileWriter)) {
      printWriter => printWriter.println(textData)
    }
  }

etc.

Jus12
fonte
39
Não me interpretem mal, eu gosto do seu código e é muito educativo, mas quanto mais vejo essas construções para problemas simples, mais isso me lembra a velha piada do "olá mundo": ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-) (+1 voto meu).
greenoldman
4
Se você está escrevendo one-liners, nada importa. Se você estiver escrevendo programas significativos (grandes com uma necessidade contínua de manutenção e evolução), esse tipo de pensamento leva ao tipo mais rápido e pernicioso de degradação da qualidade do software.
Randall Schulz
3
Nem todo mundo vai ter "olhos scala" até que algum nível de prática - é engraçado ver este exemplo de código é proveniente de "começo" Scala
asyncwait
asyncwait scala "inicial" ... o título mais irônico de todos os tempos, nota: eu tenho o livro ... e agora estou começando a entendê-lo ... suponho que eu estava um passo antes de "iniciante" lol: D ........
user1050817
1
O problema é menos os truques do Scala aqui, mas a verbosidade e o estilo pobre. Eu editei isso para muito mais legível. Após o refator, são apenas 4 linhas (bem, 4 com comprimentos de linha IDE, usadas 6 aqui para caber na tela). IMHO agora é uma resposta muito agradável.
samthebest
38

Uma resposta simples:

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
fonte
1
@samthebest você poderia adicionar as bibliotecas importde onde?
Daniel
1
A partir do java 7, use java.nio.file: def writeToFile (arquivo: String, stringToWrite: String): Unidade = {val writer = Files.newBufferedWriter (Paths.get (file)) tente writer.write (stringToWrite) finalmente writer.close ()}
E Shindler,
20

Dando outra resposta, porque minhas edições de outras respostas foram rejeitadas.

Esta é a resposta mais concisa e simples (semelhante à de Garret Hall)

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

É semelhante ao Jus12, mas sem a verbosidade e com o estilo de código correto

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

Observe que você NÃO precisa de chaves try finally, nem lambdas, e observe o uso da sintaxe do espaço reservado. Observe também uma melhor nomeação.

samthebest
fonte
2
Desculpe, mas seu código é imaginável, mas não preenche os implementedpré - requisitos. Você não pode usar o código que não está implementado. Quero dizer que você deve dizer como encontrá-lo, pois ele não está disponível por padrão e não é conhecido.
Val
15

Aqui está uma linha concisa usando a biblioteca de compiladores Scala:

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

Como alternativa, se você quiser usar as bibliotecas Java, poderá fazer isso:

Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
Garrett Hall
fonte
O que importa? ou seja, de onde vem o arquivo?
precisa
A biblioteca do compilador Scala.
Garrett Hall
3
Não é mais viável (não no Scala 2.11)
Brent Faust
1
Do que você está falando? scala.tools.nsc.io.File("/tmp/myFile.txt")trabalha no Scala 2.11.8.
1
Agora, está em scala.reflect.io.File
Keith Nordstrom
13

Forros um para salvar / ler de / para String, usando 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
}

Isso não é adequado para arquivos grandes, mas fará o trabalho.

Alguns links:

java.nio.file.Files.write
java.lang.String.getBytes
scala.collection.JavaConverters
scala.collection.immutable.List.mkString

Nick Zalutskiy
fonte
Por que isso não é adequado para arquivos grandes?
Chetan Bhasin
2
@ChetanBhasin Provavelmente porque writecopiará contentspara uma nova matriz de bytes em vez de transmiti-la para o arquivo, assim, no auge, usando duas vezes mais memória do que contentssozinha.
Daniel Werner
10

Infelizmente para a resposta principal, o Scala-IO está morto. Se você não se importa em usar uma dependência de terceiros, considere usar minha biblioteca OS-Lib . Isso facilita muito o trabalho com arquivos, caminhos e sistema de arquivos:

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

Possui uma linha para gravar arquivos , anexar arquivos , sobrescrever arquivos e muitas outras operações úteis / comuns

Li Haoyi
fonte
Obrigado por esta atualização. Votado. Mencionei sua resposta acima para obter mais visibilidade.
VonC 26/05/19
7

Uma micro biblioteca que escrevi: https://github.com/pathikrit/better-files

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

ou

file << "Hello" << "\n" << "World"
pathikrit
fonte
Aqui também - Esta questão é uma das principais novidades ao pesquisar como escrever um arquivo com o scala - agora que seu projeto ficou maior, você pode expandir um pouco sua resposta?
As6
6

Começando Scala 2.13, a biblioteca padrão fornece um utilitário de gestão de recursos dedicado: Using.

Ele pode ser usado nesse caso com recursos como PrintWriterou BufferedWriterque se estendem AutoCloseablepara gravar em um arquivo e, não importa o quê, feche o recurso posteriormente:

  • Por exemplo, com 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)
    }
  • Ou com 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"))
    }
Xavier Guihot
fonte
6

ATUALIZAÇÃO em 2019 / Sep / 01:

  • Começando com o Scala 2.13, prefira usar scala.util.
  • Corrigido o erro em finallyque engoliria o original Exceptionjogado tryse o finallycódigo lançasse umException

Depois de revisar todas essas respostas sobre como escrever facilmente um arquivo no Scala, e algumas delas são bastante legais, tive três problemas:

  1. Na resposta do Jus12 , o uso de currying no método helper usando não é óbvio para iniciantes no Scala / FP
  2. Precisa encapsular erros de nível inferior com scala.util.Try
  3. Precisa mostrar aos desenvolvedores Java novos no Scala / FP como aninhar corretamente recursos dependentes para que o closemétodo seja executado em cada recurso dependente na ordem inversa - Nota: fechar recursos dependentes na ordem inversa ESPECIALMENTE EM CASO DE FALHA é um requisito raramente compreendido a java.lang.AutoCloseableespecificação que tende a levar a erros muito perniciosos e difíceis de encontrar e falhas no tempo de execução

Antes de começar, meu objetivo não é concisão. É para facilitar um entendimento mais fácil para iniciantes do Scala / FP, geralmente aqueles provenientes de Java. No final, reunirei todos os bits e aumentarei a concisão.

Primeiro, o usingmétodo precisa ser atualizado para uso Try(novamente, concisão não é o objetivo aqui). Ele será renomeado para 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
              }
          }
      }
    )

O início do tryUsingAutoCloseablemétodo acima pode ser confuso porque parece ter duas listas de parâmetros em vez da lista de parâmetros únicos habitual. Isso é chamado de curry. E não entrarei em detalhes como o curry funciona ou onde é ocasionalmente útil. Acontece que, para esse espaço problemático específico, é a ferramenta certa para o trabalho.

Em seguida, precisamos criar o método,, tryPrintToFileque criará um (ou substituirá um existente) Filee gravará um List[String]. Ele usa um FileWriterque é encapsulado por um BufferedWriterque, por sua vez, é encapsulado por a PrintWriter. E para elevar o desempenho, BufferedWriteré definido um tamanho de buffer padrão muito maior que o padrão para defaultBufferSize, e atribuído o valor 65536.

Aqui está o código (e novamente, concisão não é o objetivo aqui):

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

O tryPrintToFilemétodo acima é útil, pois recebe uma List[String]entrada como e envia para a File. Vamos agora criar um tryWriteToFilemétodo que pega a Stringe grava em a File.

Aqui está o código (e eu vou deixar você adivinhar a prioridade da concisão aqui):

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

Finalmente, é útil poder buscar o conteúdo de a Filecomo a String. Embora scala.io.Sourceforneça um método conveniente para obter facilmente o conteúdo de a File, o closemétodo deve ser usado Sourcepara liberar os identificadores subjacentes da JVM e do sistema de arquivos. Se isso não for feito, o recurso não será liberado até que o JVM GC (Garbage Collector) saia para liberar a Sourceinstância em si. E mesmo assim, há apenas uma garantia fraca da JVM de que o finalizemétodo será chamado pelo GC para closeo recurso. Isso significa que é responsabilidade do cliente chamar explicitamente o closemétodo, da mesma forma que é responsabilidade de um cliente contar closecom uma instância dejava.lang.AutoCloseable. Para isso, precisamos de uma segunda definição do método using que manipula scala.io.Source.

Aqui está o código para isso (ainda não sendo conciso):

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

E aqui está um exemplo de uso dele em um leitor de arquivos de streaming de linha super simples (atualmente usando para ler arquivos delimitados por tabulações da saída do banco de dados):

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

Uma versão atualizada da função acima foi fornecida como resposta a uma pergunta StackOverflow diferente, mas relacionada .


Agora, reunindo tudo isso com as importações extraídas (facilitando a colagem na planilha Scala presente no plug-in Eclipse ScalaIDE e IntelliJ Scala para facilitar o despejo de saída na área de trabalho para ser examinado mais facilmente com um editor de texto), é assim que o código se parece (com maior concisão):

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

Como um novato no Scala / FP, eu queimei muitas horas (principalmente na frustração de arranhar a cabeça), ganhando o conhecimento e as soluções acima. Espero que isso ajude outros iniciantes do Scala / FP a superar esse problema de aprendizado mais rápido.

chaotic3quilibrium
fonte
2
Atualização incrível. O único problema é que agora você tem 100 linhas de código que podem ser substituídas try-catch-finally. Ainda amo sua paixão.
Observador
1
@Observer Eu diria que é uma declaração imprecisa. O padrão que estou descrevendo está na verdade reduzindo a quantidade de clichê que um cliente deve escrever para garantir o manuseio adequado do fechamento de AutoCloseables, além de ativar o padrão FP idiomático do Scala usando scala.util.Try. Se você tentar obter os mesmos efeitos que eu, escrevendo manualmente os blocos try / catch / finalmente, acho que você encontrará um pouco mais de clichê do que imagina. Portanto, há um valor significativo de legibilidade ao empurrar todo o padrão para as 100 linhas da função Scala.
chaotic3quilibrium 26/09/16
1
Desculpe se isso soou ofensivo de alguma forma. Ainda assim, o que quero dizer é que não há necessidade de tanta quantidade de código, porque o mesmo poderia ser alcançado por meio de uma abordagem não funcional com muito mais simplicidade. Pessoalmente, eu escreveria try-finalmente com algumas verificações adicionais. É apenas mais curto. Se eu quisesse usar wrappers, o ApacheUtils estará lá para usar todo o trabalho sujo. Além disso, todos os Leitores / Gravadores padrão fecham os fluxos subjacentes, de modo que seu empacotamento múltiplo não é necessário. PS: Alterei meu voto de menos um para mais um para apoiar seus esforços. Então, por favor, não me suspeite de más intenções.
Observer
Nenhuma ofensa tomada.
chaotic3quilibrium 26/09/16
1
Eu entendo seu ponto de vista. Obrigado pela discussão, eu tenho que pensar um pouco. Tenha um bom dia!
Observador
3

Aqui está um exemplo de gravação de algumas linhas em um arquivo usando o 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
Chris Martin
fonte
1

Para superar o melhor e os colaboradores antes dele, melhorei a nomeação e a concisão:

  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))
Epicurista
fonte
Isso usa "digitação de pato", que depende da reflexão. Para muitos contextos, dependendo da reflexão não é necessário começar.
chaotic3quilibrium
1

Sem dependências, com tratamento de erros

  • Usa métodos da biblioteca padrão exclusivamente
  • Cria diretórios para o arquivo, se necessário
  • Usa Eitherpara tratamento de erros

Código

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

Uso

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")
}
Matthias Braun
fonte
1

Atualização de 2019:

Resumo - O Java NIO (ou NIO.2 para assíncrono) ainda é a solução de processamento de arquivos mais abrangente suportada no Scala. O código a seguir cria e grava algum texto em um novo arquivo:

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. Importar bibliotecas Java: IO e NIO
  2. Crie um Path objeto com o nome do arquivo escolhido
  3. Converta o texto que você deseja inserir em um arquivo em uma matriz de bytes
  4. Obtenha seu arquivo como um fluxo: OutputStream
  5. Passe sua matriz de bytes para a writefunção do fluxo de saída
  6. Feche o fluxo
Janac Meena
fonte
1

Semelhante a esta resposta , aqui está um exemplo com fs2(versão 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)

  }
}
Valy Dia
fonte
0

Essa linha ajuda a escrever um arquivo de uma matriz ou sequência.

 new PrintWriter(outputPath) { write(ArrayName.mkString("")); close }
Vickyster
fonte
0

Se você tem o Akka Streams no seu projeto, ele fornece uma única linha:

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

Documentos> Streaming IO do arquivo

akauppi
fonte