Bagaimana cara menulis ke file di Scala?

157

Untuk membaca, ada abstraksi yang bermanfaat Source. Bagaimana saya bisa menulis baris ke file teks?

yura
sumber
1
Jika Anda tahu bagaimana melakukannya di Jawa maka Anda dapat menggunakan yang sama di Scala. Apakah pertanyaan Anda khusus dengan perpustakaan standar Scala?
ketika
1
@watiati ya cara terbaik untuk melakukan ini di scala
yura
Perpustakaan ini benar-benar bagus: github.com/pathikrit/better-files
Robin
Perpustakaan OS-Lib Lihaoyi github.com/lihaoyi/os-lib
WeiChing 林 煒 清

Jawaban:

71

Sunting 2019 (8 tahun kemudian), Scala-IO menjadi sangat tidak aktif, jika ada, Li Haoyi menyarankan perpustakaannya sendiri lihaoyi/os-lib, yang dia sajikan di bawah ini .

Juni 2019, Xavier Guihot menyebutkan dalam jawabannya perpustakaan Using, sebuah utilitas untuk melakukan manajemen sumber daya otomatis.


Sunting (September 2011): sejak Eduardo Costa bertanya tentang Scala2.9, dan sejak Rick-777 berkomentar bahwa scalax.IO mencatat sejarah hampir tidak ada sejak pertengahan 2009 ...

Scala-IO telah berubah tempat: lihat repo GitHub -nya, dari Jesse Eichar (juga di SO ):

Proyek payung IO Scala terdiri dari beberapa sub proyek untuk berbagai aspek dan ekstensi IO.
Ada dua komponen utama Scala IO:

  • Core - Core terutama berkaitan dengan Membaca dan menulis data ke dan dari sumber yang sewenang-wenang dan tenggelam. Ciri-ciri batu sudut adalah Input, Outputdan Seekableyang menyediakan API inti.
    Kelas penting lainnya adalah Resource, ReadCharsdan WriteChars.
  • File - File adalah File(disebut Path) API yang didasarkan pada kombinasi sistem file Java 7 NIO dan SBT PathFinder API.
    Pathdan FileSystemmerupakan titik masuk utama ke dalam 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)

Jawaban asli (Januari 2011), dengan tempat lama untuk scala-io:

Jika Anda tidak ingin menunggu Scala2.9, Anda dapat menggunakan perpustakaan scala-incubator / scala-io .
(seperti yang disebutkan dalam " Mengapa Sumber Scala tidak menutup InputStream yang mendasarinya? ")

Lihat sampel

{ // 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
sumber
15
Bagaimana dengan versi Scala 2.9? :)
Eduardo Costa
Proyek scalax tampaknya sudah mati (tidak ada komitmen sejak Juni 2009). Apakah ini benar? scalax commit history
Rick-777
@Eduardo: Saya telah menyelesaikan jawaban saya dengan tempat baru untuk perpustakaan scala-io (yang telah diperbarui untuk Scala2.9: github.com/jesseeichar/scala-io/issues/20 )
VonC
10
Apakah ini benar-benar saran saat ini untuk Scala 2.10? Gunakan Scala IO? Belum ada apa-apa di inti Scala?
Phil
2
Saya tidak pernah menggunakan scalax.io, tetapi kalau dilihat dari contoh-contoh ini, sepertinya desain API-nya sangat buruk. Mencampur metode untuk data karakter dan biner dalam satu antarmuka sedikit masuk akal dan kemungkinan besar akan mengarah ke bug pengkodean yang sulit ditemukan. Desain java.io (Reader / Writer vs. InputStream / OutputStream) tampaknya jauh lebih baik.
jcsahnwaldt Reinstate Monica
211

Ini adalah salah satu fitur yang hilang dari Scala standar yang saya temukan sangat berguna sehingga saya menambahkannya ke perpustakaan pribadi saya. (Anda mungkin harus memiliki perpustakaan pribadi juga.) Kode seperti ini:

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

dan digunakan seperti ini:

import java.io._
val data = Array("Five","strings","in","a","file!")
printToFile(new File("example.txt")) { p =>
  data.foreach(p.println)
}
Rex Kerr
sumber
1
java.io.PrintWriter () baru menggunakan pengkodean platform default, yang mungkin berarti bahwa file hasil tidak terlalu portabel. Misalnya, jika Anda ingin menghasilkan file yang nantinya dapat Anda kirim melalui email, Anda mungkin harus menggunakan konstruktor PrintWriter yang memungkinkan Anda untuk menentukan pengkodean.
jcsahnwaldt Reinstate Monica
@JonaChristopherSahnwaldt - Tentu, dalam kasus khusus Anda mungkin ingin menentukan pengkodean. Default untuk platform adalah standar yang paling masuk akal. Sama seperti dengan Source(penyandian standar secara default). Anda tentu saja dapat menambahkan misalnya enc: Option[String] = Noneparameter setelah fjika Anda menemukan ini sebagai kebutuhan umum.
Rex Kerr
6
@RexKerr - Saya tidak setuju. Seseorang harus menentukan pengkodean di hampir semua kasus. Kebanyakan kesalahan penyandian yang saya temui terjadi karena orang tidak mengerti atau tidak berpikir tentang penyandian. Mereka menggunakan default dan bahkan tidak mengetahuinya karena terlalu banyak API membiarkan mereka melakukannya. Saat ini, standar yang paling masuk akal mungkin adalah UTF-8. Mungkin Anda hanya bekerja dengan bahasa Inggris dan bahasa lain yang dapat ditulis dalam ASCII. Beruntunglah anda. Saya tinggal di Jerman dan harus memperbaiki lebih banyak umlaut yang rusak daripada yang saya ingat.
jcsahnwaldt Reinstate Monica
3
@JonaChristopherSahnwaldt - Ini adalah alasan untuk memiliki penyandian default yang masuk akal, bukan untuk memaksa semua orang untuk menentukannya setiap saat. Tetapi jika Anda menggunakan Mac dan file Anda yang ditulis oleh Java adalah gobbledygook karena mereka bukan Mac OS Roman yang dikodekan, saya tidak yakin itu lebih baik daripada membahayakan. Saya pikir itu kesalahan platform bahwa mereka belum menyetujui charset. Sebagai pengembang individu, mengetikkan string benar-benar tidak akan menyelesaikan masalah. (Semua pengembang setuju pada UTF-8 akan, tapi kemudian itu bisa masuk sebagai default.)
Rex Kerr
@JonaChristopherSahnwaldt +10 untuk memperbaiki semua umlaut yang rusak. Tidak bisa menggunakan palu, mungkin membuat lubang? Atau apakah mereka sudah lubang yang perlu diisi, mungkin orang ini dapat membantu youtube.com/watch?v=E-eBBzWEpwE Tapi serius, pengaruh ASCII sangat merusak di dunia, setuju itu harus ditentukan, dan default sebagai UTF- 8
Davos
50

Serupa dengan jawaban oleh Rex Kerr, tetapi lebih umum. Pertama saya menggunakan fungsi pembantu:

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

Maka saya menggunakan ini sebagai:

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

dan

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

dll.

Jus12
sumber
39
Jangan salah paham, saya suka kode Anda dan ini sangat mendidik, tetapi semakin saya melihat konstruksi seperti itu untuk masalah sederhana, semakin mengingatkan saya tentang lelucon "hello world" lama: ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-) (+1 memberi suara dari saya).
greenoldman
4
Jika Anda menulis satu kalimat, tidak ada masalah sama sekali. Jika Anda menulis program penting (besar dengan kebutuhan pemeliharaan dan evolusi yang berkelanjutan), pemikiran seperti ini mengarah pada jenis penurunan kualitas perangkat lunak yang paling cepat dan merusak.
Randall Schulz
3
Tidak semua orang akan memiliki "mata scala" sampai beberapa tingkat latihan - lucu melihat contoh kode ini berasal dari "Awal" Scala
asyncwait
asyncunggu scala "permulaan" ... judul yang paling ironis, catatan: Saya sudah bukunya ... dan baru saja saya mulai memahaminya .. Saya kira daripada saya selangkah sebelum "pemula" lol: D ........
user1050817
1
Masalahnya bukan trik Scala di sini, tetapi verbositas dan gaya yang buruk. Saya telah mengedit ini agar lebih mudah dibaca. Setelah refactor saya itu hanya 4 baris (yah, 4 dengan panjang garis IDE, digunakan 6 di sini agar sesuai dengan layar). IMHO itu sekarang jawaban yang sangat bagus.
samthebest
38

Jawaban sederhana:

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
sumber
1
@samthebest dapatkah Anda menambahkan perpustakaan tempat Anda importberasal?
Daniel
1
Pada java 7, gunakan java.nio.file sebagai gantinya: def writeToFile (file: String, stringToWrite: String): Unit = {val writer = Files.newBufferedWriter (Paths.get (file)) coba writer.write (stringToWrite) akhirnya writer.close ()}
E Shindler
20

Memberikan jawaban lain, karena hasil edit saya dari jawaban lain ditolak.

Ini adalah jawaban yang paling ringkas dan sederhana (mirip dengan Garret Hall)

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

Ini mirip dengan Jus12, tetapi tanpa verbosity dan dengan gaya kode yang benar

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

Perhatikan bahwa Anda TIDAK membutuhkan kurung kurawal untuk try finally, atau lambda, dan perhatikan penggunaan sintaksis tempat penampung. Perhatikan juga penamaan yang lebih baik.

samthebest
sumber
2
Maaf, tetapi kode Anda bisa dibayangkan, itu tidak memenuhi implementedprasyarat. Anda tidak dapat menggunakan kode yang tidak diterapkan. Maksud saya, Anda harus memberi tahu cara menemukannya karena tidak tersedia secara default dan tidak terkenal.
Val
15

Berikut ini adalah kalimat singkat yang menggunakan pustaka kompiler Scala:

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

Atau, jika Anda ingin menggunakan perpustakaan Java Anda bisa melakukan hack ini:

Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
Garrett Hall
sumber
Impor apa? yaitu dari mana File berasal?
Ben Hutchison
Pustaka kompilator Scala.
Garrett Hall
3
Tidak lagi layak (tidak dalam Scala 2.11)
Brent Faust
1
Apa yang kamu bicarakan? scala.tools.nsc.io.File("/tmp/myFile.txt")bekerja di Scala 2.11.8.
1
Sekarang di scala.reflect.io.File
Keith Nordstrom
13

Satu liner untuk menyimpan / membaca ke / dari String, menggunakan 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
}

Ini tidak cocok untuk file besar, tetapi akan melakukan pekerjaan.

Beberapa tautan:

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

Nick Zalutskiy
sumber
Mengapa ini tidak cocok untuk file besar?
Chetan Bhasin
2
@ ChetanBhasin Mungkin karena writeakan menyalin contentske array byte baru alih-alih streaming ke file, sehingga pada puncaknya menggunakan memori dua kali lebih banyak daripada contentssendirian.
Daniel Werner
10

Sayangnya untuk jawaban teratas, Scala-IO sudah mati. Jika Anda tidak keberatan menggunakan ketergantungan pihak ketiga, pertimbangkan untuk menggunakan perpustakaan OS-Lib saya . Ini membuat bekerja dengan file, jalur dan sistem file sangat mudah:

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

Ini memiliki satu-baris untuk menulis ke file , menambahkan file , menimpa file , dan banyak operasi lain yang berguna / umum

Li Haoyi
sumber
Terima kasih atas pembaruan ini. Terpilih. Saya telah merujuk jawaban Anda di atas di atas untuk visibilitas yang lebih baik.
VonC
7

Perpustakaan mikro yang saya tulis: https://github.com/pathikrit/better-files

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

atau

file << "Hello" << "\n" << "World"
pathikrit
sumber
Di sini juga - Pertanyaan ini adalah salah satu hits teratas ketika googling bagaimana menulis file dengan scala - sekarang proyek Anda telah menjadi lebih besar, Anda mungkin ingin sedikit memperluas jawaban Anda?
asac
6

Mulai Scala 2.13, perpustakaan standar menyediakan utilitas manajemen sumber daya khusus: Using.

Ini dapat digunakan dalam kasus ini dengan sumber daya seperti PrintWriteratau BufferedWriteryang meluas AutoCloseableuntuk menulis ke file dan, apa pun yang terjadi, tutup sumber daya setelahnya:

  • Misalnya, dengan 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)
    }
  • Atau dengan 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
sumber
6

DIPERBARUI pada 2019 / Sep / 01:

  • Dimulai dengan Scala 2.13, lebih suka menggunakan scala.util. Menggunakan
  • Fixed bug di mana finallyakan menelan asli Exceptiondilemparkan oleh tryjika finallykode melemparkanException

Setelah meninjau semua jawaban ini tentang cara mudah menulis file di Scala, dan beberapa di antaranya cukup bagus, saya punya tiga masalah:

  1. Dalam jawaban Jus12 , penggunaan currying untuk metode helper tidak jelas bagi pemula Scala / FP
  2. Perlu merangkum kesalahan tingkat yang lebih rendah dengan scala.util.Try
  3. Perlu memperlihatkan pengembang Java yang baru ke Scala / FP bagaimana cara menyarangkan sumber daya dependen dengan benar sehingga closemetode ini dilakukan pada setiap sumber daya dependen dalam urutan terbalik - Catatan: menutup sumber daya dependen dalam urutan terbalik. TERUTAMA DALAM ACARA KEGAGALAN adalah persyaratan yang jarang dipahami oleh yang java.lang.AutoCloseablespesifikasi yang cenderung menyebabkan sangat merusak dan sulit untuk menemukan bug dan kegagalan jangka waktu

Sebelum memulai, tujuan saya bukanlah keringkasan. Ini untuk memfasilitasi pemahaman yang lebih mudah bagi pemula Scala / FP, biasanya yang berasal dari Jawa. Pada akhirnya, saya akan mengumpulkan semua bit, dan kemudian meningkatkan keringkasannya.

Pertama, usingmetode ini perlu diperbarui untuk digunakan Try(sekali lagi, keringkasan bukanlah tujuan di sini). Itu akan diubah namanya menjadi 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
              }
          }
      }
    )

Awal tryUsingAutoCloseablemetode di atas mungkin membingungkan karena tampaknya memiliki dua daftar parameter, bukan daftar parameter tunggal biasa. Ini disebut kari. Dan saya tidak akan menjelaskan secara terperinci bagaimana kari bekerja atau di mana kadang berguna. Ternyata untuk ruang masalah khusus ini, ini adalah alat yang tepat untuk pekerjaan itu.

Selanjutnya, kita perlu membuat metode,, tryPrintToFileyang akan membuat (atau menimpa yang sudah ada) Filedan menulis a List[String]. Ini menggunakan FileWriteryang dienkapsulasi oleh BufferedWriteryang pada gilirannya dienkapsulasi oleh a PrintWriter. Dan untuk meningkatkan kinerja, ukuran buffer standar jauh lebih besar dari standar untuk BufferedWriterdidefinisikan,defaultBufferSize ,, dan diberi nilai 65536.

Inilah kodenya (dan sekali lagi, keringkasan bukanlah tujuan di sini):

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

tryPrintToFileMetode di atas berguna karena dibutuhkan List[String]input sebagai dan mengirimkannya ke a File. Sekarang mari kita membuat tryWriteToFilemetode yang mengambil Stringdan menulisnya ke File.

Berikut kodenya (dan saya akan membiarkan Anda menebak prioritas keringkasan di sini):

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

Akhirnya, berguna untuk dapat mengambil konten Filesebagai String. Meskipun scala.io.Sourcemenyediakan metode yang mudah untuk mendapatkan konten a File, closemetode tersebut harus digunakan pada Sourceuntuk melepaskan JVM dan sistem file yang menangani. Jika ini tidak dilakukan, maka sumber daya tidak dirilis sampai JVM GC (Pengumpul Sampah) sempat merilis Sourceinstance itu sendiri. Dan bahkan kemudian, hanya ada jaminan JVM yang lemah bahwa finalizemetode tersebut akan dipanggil oleh GC ke closesumber daya. Ini berarti bahwa itu adalah tanggung jawab klien untuk secara eksplisit memanggil closemetode, sama seperti itu adalah tanggung jawab klien untuk menilai closepada contoh darijava.lang.AutoCloseable. Untuk ini, kita memerlukan definisi kedua dari metode using yang menangani scala.io.Source.

Berikut kode untuk ini (masih belum ringkas):

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

Dan berikut ini adalah contoh penggunaannya dalam pembaca file streaming baris super sederhana (saat ini digunakan untuk membaca file yang dibatasi-tab dari output basis data):

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

Versi terbaru dari fungsi di atas telah disediakan sebagai jawaban untuk pertanyaan StackOverflow yang berbeda namun terkait .


Sekarang, menggabungkan semua itu dengan impor yang diekstraksi (membuatnya lebih mudah untuk ditempelkan ke lembar kerja Scala yang ada di plugin Eclipse ScalaIDE dan IntelliJ Scala untuk membuatnya lebih mudah untuk membuang output ke desktop agar lebih mudah diperiksa dengan editor teks), seperti inilah bentuk kode (dengan peningkatan keringkasan):

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

Sebagai pemula Scala / FP, saya telah menghabiskan banyak waktu (sebagian besar frustrasi kepala-menggaruk) mendapatkan pengetahuan dan solusi di atas. Saya harap ini membantu pemula Scala / FP lainnya mengatasi hump belajar ini lebih cepat.

chaotic3quilibrium
sumber
2
Pembaruan luar biasa. Satu-satunya masalah adalah bahwa sekarang Anda memiliki 100 baris kode yang dapat diganti try-catch-finally. Cintailah gairah Anda.
Pengamat
1
@ Observer Saya akan menegaskan bahwa ini adalah pernyataan yang tidak akurat. Pola yang saya jelaskan sebenarnya mengurangi jumlah boilerplate yang harus ditulis klien untuk memastikan penanganan penutupan AutoCloseables yang tepat sambil juga mengaktifkan pola FP idiomatik Scala menggunakan scala.util.Try. Jika Anda mencoba untuk mencapai efek yang sama saya miliki dengan secara manual menuliskan blok coba / tangkap / akhirnya, saya pikir Anda akan menemukan Anda berakhir dengan boilerplate sedikit lebih banyak daripada yang Anda bayangkan. Jadi, ada nilai keterbacaan yang signifikan dalam mendorong semua boilerplate ke dalam 100 baris fungsi Scala.
chaotic3quilibrium
1
Maaf jika itu terdengar tidak sopan. Namun, maksud saya adalah bahwa tidak perlu dalam jumlah kode seperti itu, karena hal yang sama dapat dicapai melalui pendekatan non-fungsional dengan lebih banyak kesederhanaan. Secara pribadi, saya akan menulis coba-akhirnya dengan beberapa cek tambahan. Itu lebih pendek. Jika saya ingin menggunakan pembungkus, ApacheUtils ada di sana untuk menggunakan semua pekerjaan kotor. Terlebih lagi, semua Pembaca / Penulis standar menutup aliran yang mendasarinya sehingga multipwrap Anda tidak diperlukan. PS: Saya sudah mengubah suara saya dari minus satu ke plus satu untuk mendukung upaya Anda. Jadi, tolong, jangan mencurigai saya dengan niat buruk.
Pengamat
Jangan tersinggung.
chaotic3quilibrium
1
Saya mengerti sudut pandang Anda. Terima kasih untuk diskusi, saya harus memikirkannya sedikit. Semoga harimu menyenangkan!
Pengamat
3

Berikut adalah contoh penulisan beberapa baris ke file menggunakan 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
sumber
1

Untuk melampaui samthebest dan kontributor di depannya, saya telah meningkatkan penamaan dan keringkasan:

  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
sumber
Ini menggunakan "mengetik bebek" yang tergantung pada refleksi. Untuk banyak konteks, tergantung pada refleksi bukanlah hal yang baru.
chaotic3quilibrium
1

Tidak ada ketergantungan, dengan penanganan kesalahan

  • Menggunakan metode dari perpustakaan standar secara eksklusif
  • Membuat direktori untuk file, jika perlu
  • Penggunaan Eitheruntuk penanganan kesalahan

Kode

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

Pemakaian

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
sumber
1

Pembaruan 2019:

Ringkasan - Java NIO (atau NIO.2 untuk async) masih merupakan solusi pemrosesan file paling komprehensif yang didukung oleh Scala. Kode berikut membuat dan menulis beberapa teks ke file baru:

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. Impor perpustakaan Java: IO dan NIO
  2. Buat Pathobjek dengan nama file yang Anda pilih
  3. Konversikan teks Anda yang ingin Anda masukkan ke file menjadi array byte
  4. Dapatkan file Anda sebagai aliran: OutputStream
  5. Pass array byte Anda ke writefungsi aliran output Anda
  6. Tutup alirannya
Janac Meena
sumber
1

Mirip dengan jawaban ini , berikut adalah contoh dengan fs2(versi 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
sumber
0

Baris ini membantu untuk menulis file dari Array atau String.

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

Jika Anda memiliki Akka Streams di proyek Anda, itu menyediakan satu-liner:

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

Akka docs> Streaming File IO

aliasuppi
sumber