Scala'da bir dosyaya nasıl yazılır?


157

Okumak için yararlı soyutlama var Source. Bir metin dosyasına nasıl satır yazabilirim?


1
Java'da bunu nasıl yapacağınızı biliyorsanız, Scala'da da bunu kullanabilirsiniz. Sorunuz özellikle Scala standart kütüphanesiyle mi ilgili?
wheaties

1
@wheaties evet bunu scala'da yapmanın en iyi yolu
yura

Bu kütüphane gerçekten çok iyi: github.com/pathikrit/better-files
Robin

Lihaoyi'nin OS-Lib kütüphanesi github.com/lihaoyi/os-lib
WeiChing 林 煒 清

Yanıtlar:


71

Düzen 2019 (8 yıl sonra), Scala-IO varsa çok aktif değil varlık Li Haoyi kendi kütüphanesini anlaşılacağı lihaoyi/os-libo, aşağıda sunulur .

Haziran 2019, Xavier Guihot bahsedildi onun cevabını kütüphanesini Using, otomatik kaynak yönetimini gerçekleştirmek için bir yardımcı program.


Düzen (Eylül 2011): beri Eduardo Costa Scala2.9 sorar, ve o zamandan beri Rick-777 olduğunu comments scalax.IO geçmişini taahhüt mid-2009'dan beri hemen hemen yok denecek kadar ...

Scala-IO değiştirilen yere sahiptir: bkz GitHub Repo gelen, Jesse Eichar (ayrıca vb ):

Scala IO şemsiye projesi, IO'nun farklı yönleri ve uzantıları için birkaç alt projeden oluşmaktadır.
Scala IO'nun iki ana bileşeni vardır:

  • Çekirdek - Çekirdek öncelikle keyfi kaynaklara ve lavabolara veri okuma ve yazma ile ilgilenir. Köşe taşı özellikleri vardır Input, Outputve Seekablehangi çekirdek API sağlarlar.
    Öneme sahip diğer sınıfları vardır Resource, ReadCharsve WriteChars.
  • Dosya - Dosya, Java 7 NIO dosya sistemi ve SBT PathFinder API'lerinin bir kombinasyonunu temel alan File(denilen Path) bir API'dir.
    Pathve FileSystemScala IO Dosya API'sındaki ana giriş noktalarıdır.
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)

Orijinal cevap (Ocak 2011), eski skala yeri ile:

Scala2.9'u beklemek istemiyorsanız, scala-inkübatör / scala-io kütüphanesini kullanabilirsiniz.
(" Scala Kaynağı temeldeki InputStream'i neden kapatmıyor? " bölümünde belirtildiği gibi )

Örneklere bakın

{ // 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 sürümü ne olacak? :)
Eduardo Costa

Skalaks projesi ölü gibi görünüyor (Haziran 2009'dan bu yana taahhüt yok). Bu doğru mu? scalax taahhüt geçmişi
Rick-777

@Eduardo: Sca2io kütüphanesi için yeni yerle yanıtımı tamamladım (Scala2.9 için güncellendi: github.com/jesseeichar/scala-io/issues/20 )
VonC

10
Bu gerçekten Scala 2.10 için geçerli öneri mi? Scala IO kullanılsın mı? Scala'nın çekirdeğinde henüz bir şey yok mu?
Phil

2
Hiçbir zaman scalax.io kullanmadım, ancak bu örnek satırlardan yola çıkarak, API tasarımının oldukça kötü olduğu anlaşılıyor. Karakter ve ikili veriler için bir arayüzde karıştırma yöntemleri çok az mantıklıdır ve bulunması zor olan kodlama hatalarına yol açacaktır. Java.io'nun tasarımı (Reader / Writer ve InputStream / OutputStream) çok daha iyi görünüyor.
jcsahnwaldt Reinstate Monica

211

Bu, standart Scala'da eksik olan özelliklerden biri, kişisel kütüphaneme eklemem için çok yararlı buldum. (Muhtemelen kişisel bir kütüphaneniz de olmalıdır.) Kod şöyle:

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

ve şu şekilde kullanılır:

import java.io._
val data = Array("Five","strings","in","a","file!")
printToFile(new File("example.txt")) { p =>
  data.foreach(p.println)
}

1
yeni java.io.PrintWriter () platform varsayılan kodlamasını kullanır, bu da sonuç dosyasının çok taşınabilir olmadığı anlamına gelir. Örneğin, daha sonra e-postayla gönderebileceğiniz bir dosya üretmek istiyorsanız, muhtemelen bir kodlama belirtmenize izin veren PrintWriter yapıcısını kullanmalısınız.
jcsahnwaldt Reinstate Monica

@JonaChristopherSahnwaldt - Elbette, özel durumlarda kodlamayı belirtmek isteyebilirsiniz. Platform için varsayılan, ortalama olarak en mantıklı varsayılan değerdir. İle aynı Source(varsayılan olarak varsayılan kodlama). Elbette, eğer ortak bir ihtiyaç bulursanız, örneğin bir enc: Option[String] = Noneparametre ekleyebilirsiniz f.
Rex Kerr

6
@RexKerr - Kabul etmiyorum. Hemen hemen tüm durumlarda kodlama belirtilmelidir. Karşılaştığım çoğu kodlama hatası, insanlar kodlamayı anlamıyor veya düşünmüyor. Varsayılanı kullanıyorlar ve bilmiyorlar çünkü çok fazla API ondan kaçmalarına izin veriyor. Günümüzde, en mantıklı varsayılan muhtemelen UTF-8 olacaktır. Belki sadece ASCII'de yazılabilen İngilizce ve diğer dillerle çalışıyorsunuz. Seni şanslı. Almanya'da yaşıyorum ve hatırlamak istediğimden daha fazla kırılmış lamel düzeltmek zorunda kaldım.
jcsahnwaldt Reinstate Monica

3
@JonaChristopherSahnwaldt - Bu, herkesi her zaman belirtmeye zorlamak için değil, makul bir varsayılan kodlamaya sahip olmanın bir nedenidir. Mac kullanıyorsanız ve Java tarafından yazılan dosyalarınız Mac OS Roman kodlu olmadığı için gobbledygook ise, zarardan daha iyi yaptığından emin değilim. Bence platformların hatası bir karakter seti üzerinde anlaşamadılar. Bireysel bir geliştirici olarak, bir dize yazmak gerçekten sorunu çözmeyecektir. (UTF-8'i kabul eden tüm geliştiriciler, ancak bu sadece varsayılan olarak girebilir.)
Rex Kerr

@JonaChristopherSahnwaldt +10 tüm kırık hüzmeleri düzeltmek için. Bir çekiç kullanamazsınız, belki bir delgeç? Yoksa zaten doldurulması gereken delikler mi, belki bu adam youtube.com/watch?v=E-eBBzWEpwE yardımcı olabilir , ancak cidden, ASCII'nin etkisi dünyada çok zarar verici, belirtilmesi gerektiği ve UTF- 8
Davos

50

Rex Kerr'ın cevabına benzer, ancak daha genel. İlk önce bir yardımcı işlev kullanıyorum:

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

Sonra bunu şöyle kullanın:

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

ve

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

vb.


39
Beni yanlış anlamayın, kodunuzu beğendim ve çok eğitici, ama basit sorunlar için bu tür yapıları ne kadar çok görürsem, eski "merhaba dünya" şakasını hatırlatıyor: ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-) (benden +1 oy).
greenoldman

4
Tek satır yazıyorsanız, hiçbir şey önemli değil. Önemli programlar yazıyorsanız (bakım ve evrime sürekli ihtiyaç duyan büyük), bu tür düşünme en hızlı ve zararlı yazılım kalitesinde bozulmaya yol açar.
Randall Schulz

3
Herkes belirli bir seviyeye kadar "scala gözleri" olmayacak - bu kod örneğinin "Başlangıç" Scala'dan geldiğini görmek komik
asyncwait

asyncwait "başlangıç" scala ... şimdiye kadarki en ironik başlık, not: Ben kitap var ... ve şimdi anlamaya başladım ... "acemi" lol önce bir adım olduğumu varsayalım lol: D ........
user1050817

1
Sorun burada Scala hileleri daha az, ama ayrıntı ve zayıf tarzı. Bunu çok daha okunaklı olarak düzenledim. Refaktörümden sonra sadece 4 satır (IDE hat uzunlukları ile 4, burada ekrana sığması için 6 kullanılır). IMHO şimdi çok güzel bir cevap.
samthebest

38

Basit bir cevap:

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 sizden importgelen kütüphaneleri ekleyebilir misiniz ?
Daniel

1
Java 7'den itibaren bunun yerine java.nio.file kullanın: def writeToFile (dosya: String, stringToWrite: String): Unit = {val writer = Files.newBufferedWriter (Paths.get (dosya)) sonunda writer.write (stringToWrite) komutunu deneyin writer.close ()}
E Shindler

20

Başka bir cevap vermek, çünkü reddettiğim yerlerde diğer cevapları düzenlemelerim.

Bu en özlü ve basit cevaptır (Garret Hall'unkine benzer)

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

Bu Jus12'ye benzer, ancak ayrıntısızlık ve doğru kod stili olmadan

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

Kıvırcık parantezlere try finallyveya lambdalara ihtiyacınız olmadığını ve yer tutucu sözdiziminin kullanımını not edin. Ayrıca daha iyi adlandırma not edin.


2
Maalesef kodunuz akla gelebilir, implementedön koşulu karşılamıyor . Uygulanmayan kodu kullanamazsınız. Yani, varsayılan olarak bulunmadığı ve iyi bilinmediği için onu nasıl bulacağınızı söylemelisiniz.
Val

15

İşte Scala derleyici kitaplığını kullanan özlü bir tek katmanlı:

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

Alternatif olarak, Java kitaplıklarını kullanmak istiyorsanız bu hack'i yapabilirsiniz:

Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}

Ne ithalatı? yani Dosya nereden geliyor?
Ben Hutchison

Scala derleyici kütüphanesi.
Garrett Hall

3
Artık geçerli değil (Scala 2.11'de değil)
Brent Faust

1
Neden bahsediyorsun? scala.tools.nsc.io.File("/tmp/myFile.txt")Scala'da çalışıyor 2.11.8.

1
Şu anda scala.reflect.io'da.File
Keith Nordstrom

13

StringKullanarak kaydetmek / okumak için bir astar 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
}

Bu büyük dosyalar için uygun değildir, ancak işi yapar.

Bazı bağlantılar:

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


Bu neden büyük dosyalar için uygun değil?
Chetan Bhasin

2
@ChetanBhasin Muhtemelen yeni bir bayt dizisine dosyaya aktarmak yerine writekopyalayacağından contents, contentstek başına olduğundan iki kat daha fazla bellek kullanarak zirvede .
Daniel Werner

10

Ne yazık ki en iyi cevap için Scala-IO öldü. Üçüncü tarafa bağımlı olmanın bir sakıncası yoksa, OS-Lib kütüphanemi kullanmayı düşünün . Bu, dosyalar, yollar ve dosya sistemi ile çalışmayı çok kolaylaştırır:

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

Bunun için bir gömlekler vardır dosyalara yazmak , dosyalara ekleyerek , dosyalar üzerine , ve diğer birçok faydalı / ortak operasyonlar


Bu güncelleme için teşekkür ederim. Upvoted. Daha fazla görünürlük için cevabınızı yukarıda kendim yazdım.
VonC


7

Başlangıçta Scala 2.13, standart kütüphane özel bir kaynak yönetimi yardımcı programı sağlar:Using .

Bu durumda, bir dosyaya yazmak için genişletilen PrintWriterveya BufferedWritergenişletilen kaynaklarla kullanılabilir AutoCloseableve ne olursa olsun, kaynağı daha sonra kapatın:

  • Örneğin, java.ioapi ile:

    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)
    }
  • Veya java.nioapi ile:

    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 / Eylül / 01'de GÜNCELLEME:

  • Scala 2.13 ile başlayarak, scala.util edin.
  • Sabit hata finallyorijinal yutmak istiyorum Exceptiontarafından atılan tryeğer finallykod attıException

Scala'da bir dosyayı nasıl kolayca yazabileceğinizle ilgili tüm bu cevapları inceledikten ve bazıları oldukça güzel, üç sorunum vardı:

  1. In Jus12 cevabı , yardımcı yöntem kullanma kullanımı Scala / FP yeni başlayanlar için açık değildir.
  2. Daha düşük düzeydeki hataları scala.util.Try
  3. Scala / FP'de yeni olan Java geliştiricilerine bağımlı kaynakların nasıl düzgün bir şekilde iç içe yerleştirileceğini gösterme ihtiyacı vardır, böylece closeyöntem her bağımlı kaynak üzerinde ters sırayla gerçekleştirilir - Not: BAĞIMSIZLIK OLASINDA ÖZEL OLARAK bağımlı kaynakların ters sırada kapatılması nadiren anlaşılan bir gerekliliktir java.lang.AutoCloseableçok tehlikeli ve zor yol açma eğilimindedir şartname böcek ve çalışma zamanı hataları bulmak için

Başlamadan önce amacım kısa ve öz değil. Scala / FP yeni başlayanlar için, genellikle Java'dan gelenler için daha kolay anlaşılmasını sağlamaktır. En sonunda, tüm bitleri bir araya getireceğim ve daha sonra özlüğini artıracağım.

İlk olarak, usingyöntemin kullanmak için güncellenmesi gerekir Try(yine, kısaca buradaki amaç değildir). Yeniden adlandırılacak 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
              }
          }
      }
    )

Yukarıdaki tryUsingAutoCloseableyöntemin başlangıcı kafa karıştırıcı olabilir, çünkü alışılmış tekli parametre listesi yerine iki parametre listesi var gibi görünür. Buna körelme denir. Ve körüğün nasıl çalıştığını veya zaman zaman faydalı olduğu ayrıntılara girmeyeceğim . Bu özel sorun alanı için iş için doğru araç olduğu ortaya çıkıyor.

Daha sonra, tryPrintToFilebir (veya var olanın üzerine Fileyazacak ) ve a yazacak bir yöntem oluşturmamız gerekiyor List[String]. Bir FileWriterile kapsüllenmiş olan bir kullanır, bu da a BufferedWriterile kapsüllenir PrintWriter. Performansı artırmak için BufferedWriter, varsayılan değerden çok daha büyük bir varsayılan arabellek boyutu tanımlanır,defaultBufferSize ve 65536 değeri atanır.

İşte kod (ve yine, özlem burada amaç değildir):

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

Yukarıdaki tryPrintToFileyöntem, bir List[String]as girişini alması ve a ' ya göndermesi açısından kullanışlıdır File. Şimdi tryWriteToFilea'yı alan Stringve a'ya yazan bir yöntem oluşturalım File.

İşte kod (ve burada özlemin önceliğini tahmin etmenize izin vereceğim):

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

Son olarak, a 'nın içeriğini Filea String. Birlikte scala.io.Sourcekolayca içeriğini elde etmek için uygun bir yöntem sağlar File, closeyöntem kullanılmalıdır Sourcetemel JVM ve dosya sistemi kolları serbest bırakın. Bu yapılmazsa, JVM GC (Çöp Toplayıcı) Sourceörneğin kendisini serbest bırakmaya başlayana kadar kaynak serbest bırakılmaz . Ve o zaman bile, finalizeyöntemin GC tarafından closekaynağa çağrılacağı zayıf bir JVM garantisi vardır . Bu, açıkça çağrılmak müşterinin sorumluluğundadırclose yöntemi , tıpkı bir müşterinin closeörneğinde uzun süre müşterinin sorumluluğunda olduğu gibijava.lang.AutoCloseable. Bunun için, ele alan kullanım yönteminin ikinci bir tanımına ihtiyacımız var scala.io.Source.

İşte bunun kodu (hala özlü değil):

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

Ve işte süper basit bir çizgi akışı dosya okuyucuda (şu anda sekme ile ayrılmış dosyaları veritabanı çıktısından okumak için kullanılıyor) bir örnek kullanım:

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

Bir Yukarıdaki fonksiyonun güncellenmiş sürümü bir cevap olarak sağlanmıştır farklı ancak ilişkili bir StackOverflow soru .


Şimdi, çıkarılan ithalat ile bir araya getirerek (hem bir Eclipse ScalaIDE hem de IntelliJ Scala eklentisinde bulunan Scala Çalışma Sayfasına yapıştırmayı çok daha kolay hale getirmek için, bir metin editörü ile daha kolay incelenmek üzere masaüstüne çıktı dökümü kolaylaştırmak için), kodun görünüşü budur (artan kısalık ile):

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

Bir Scala / FP acemi olarak, yukarıdaki bilgileri ve çözümleri kazanarak saatlerce (çoğunlukla kafa karıştırıcı hayal kırıklığı) yandım. Umarım bu, diğer Scala / FP yenilerinin bu özel öğrenme hızını daha hızlı aşmasına yardımcı olur.


2
İnanılmaz güncelleme. Tek sorun, şimdi değiştirilebilir 100 kod satırı gibi olmasıdır try-catch-finally. Hala tutkunu seviyorum.
Gözlemci

1
@ Gözlemci bunun yanlış bir ifade olduğunu iddia ediyorum. Tarif ettiğim kalıp aslında bir istemcinin AutoCloseable'ların kapanışının düzgün bir şekilde ele alınmasını sağlamak için yazması gereken kazan plakasını azaltırken, aynı zamanda scala.util kullanmanın Scala deyimsel FP modelini de etkinleştiriyor. Try / catch / nihayet blokları manuel olarak yazarak sahip olduğum aynı etkileri elde etmeye çalışırsanız, hayal ettiğinizden biraz daha fazla kazan plakası bulduğunuzu düşünüyorum. Bu nedenle, tüm kazan plakasını Scala fonksiyonunun 100 hattına itmede önemli okunabilirlik değeri vardır.
chaotic3quilibrium

1
Bu herhangi bir şekilde rahatsız edici geliyorsa özür dilerim. Yine de, böylesi bir koda ihtiyaç duyulmadığından, aynı şey çok daha basit bir şekilde işlevsel olmayan yaklaşımla elde edilebilir. Şahsen, bazı ek kontrollerle denemeyi yazarım. Sadece daha kısa. Eğer sarmalayıcıları kullanmak istersem, ApacheUtils tüm kirli işleri kullanmak için orada. Dahası, tüm standart Okuyucu / Yazıcılar altta yatan akışları kapatır, böylece çoklu sarmanız gerekmez. Not: Çabalarınızı desteklemek için oyumu eksi birden artı bire değiştirdim. Yani, lütfen, benden kötü niyetle şüphelenme.
Gözlemci

Alınan suç yok.
chaotic3quilibrium

1
Bakış açınızı anlıyorum. Tartıştığınız için teşekkürler, biraz düşünmeliyim. İyi günler!
Gözlemci

3

İşte bir dosyaya scalaz-stream kullanarak bazı satırlar yazma örneği .

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'i ve ondan önceki katkıda bulunanları aşmak için, isimlendirme ve kısalığı geliştirdim:

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

Bu yansıma bağlı "ördek yazarak" kullanır. Birçok bağlam için, yansımasına bağlı olarak bir başlangıç ​​değildir.
chaotic3quilibrium

1

Hata işleme ile bağımlılık yok

  • Standart kitaplıktaki yöntemleri yalnızca kullanır
  • Gerekirse dosya için dizinler oluşturur
  • Kullanımları Eitherhata işleme için

kod

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

kullanım

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 Güncellemesi:

Özet - Java NIO (veya async için NIO.2) hala Scala'da desteklenen en kapsamlı dosya işleme çözümüdür. Aşağıdaki kod, bazı metinleri yeni bir dosyaya oluşturur ve yazar:

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 kitaplıklarını içe aktarma: IO ve NIO
  2. PathSeçtiğiniz dosya adıyla bir nesne oluşturun
  3. Dosyaya eklemek istediğiniz metninizi bayt dizisine dönüştürme
  4. Dosyanızı akış olarak alın: OutputStream
  5. Bayt dizinizi çıkış akışınızın writeişlevine aktarın
  6. Akışı kapatın

1

Bu yanıta benzer şekilde , fs2(1.0.4 sürümü) ile bir örnek :

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

Bu satır, bir Dizi veya Dize'den bir dosya yazmaya yardımcı olur.

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

0

Projenizde yine de Akka Akışları varsa, bir astar sağlar:

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

Akka belgeleri> Akış Dosyası ES

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.