스칼라에서 파일에 쓰는 방법?


157

읽기에는 유용한 추상화가 Source있습니다. 텍스트 파일에 줄을 쓰려면 어떻게해야합니까?


1
Java로 그렇게하는 방법을 알고 있다면 Scala에서도 동일하게 사용할 수 있습니다. 스칼라 표준 라이브러리에 대한 질문이 있습니까?
wheaties

1
@wheaties yes scala에서 이것을하는 가장 좋은 방법
yura

이 라이브러리는 정말 좋습니다 : github.com/pathikrit/better-files
Robin

Lihaoyi의 OS-Lib 라이브러리 github.com/lihaoyi/os-lib
WeiChing 林 煒 清

답변:


71

편집 2019 (8 년 이상), 스칼라-IO가 있는 경우, 매우 활성화되지되는 리튬 Haoyi가 자신의 라이브러리를 제안 lihaoyi/os-lib그가 것을, 아래에 제시한다 .

2019 년 6 월 Xavier Guihot자신의 답변Using자동 리소스 관리를 수행하는 유틸리티 인 라이브러리를 언급했습니다 .


편집 (2011 년 9 월) : Eduardo Costa 가 Scala2.9에 대해 질문 한 후 Rick-777scalax.IO 커밋 기록 이 2009 년 중반 이후 거의 존재하지 않는다고 언급 한 이후로 ...

스칼라-IO가 변경된 곳이 : 그 볼 GitHub의의의 repo 에서 제시 Eichar (또한 SO에를 )

Scala IO 우산 프로젝트는 IO의 다양한 측면과 확장을위한 몇 가지 하위 프로젝트로 구성됩니다.
스칼라 IO에는 두 가지 주요 구성 요소가 있습니다.

  • 코어 -코어는 주로 임의의 소스 및 싱크에서 데이터를 읽고 쓰는 것을 처리합니다. 코너 스톤의 특징은 Input, Output하고 Seekable있는 핵심 API를 제공합니다.
    중요성의 다른 클래스는 Resource, ReadChars하고 WriteChars.
  • File -File은 Java 7 NIO 파일 시스템과 SBT PathFinder API의 조합을 기반으로 하는 File(라 불리는 Path) API입니다.
    PathFileSystem스칼라 IO 파일 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)

scala-io의 이전 장소가 포함 된 원본 답변 (2011 년 1 월) :

Scala2.9를 기다리지 않으려면 scala-incubator / scala-io 라이브러리를 사용할 수 있습니다 .
( " 스칼라 소스는 왜 기본 InputStream을 닫지 않습니까? "

샘플 보기

{ // several examples of writing data
    import scalax.io.{
      FileOps, Path, Codec, OpenOption}
    // the codec must be defined either as a parameter of ops methods or as an implicit
    implicit val codec = scalax.io.Codec.UTF8


    val file: FileOps = Path ("file")

    // write bytes
    // By default the file write will replace
    // an existing file with the new data
    file.write (Array (1,2,3) map ( _.toByte))

    // another option for write is openOptions which allows the caller
    // to specify in detail how the write should take place
    // the openOptions parameter takes a collections of OpenOptions objects
    // which are filesystem specific in general but the standard options
    // are defined in the OpenOption object
    // in addition to the definition common collections are also defined
    // WriteAppend for example is a List(Create, Append, Write)
    file.write (List (1,2,3) map (_.toByte))

    // write a string to the file
    file.write("Hello my dear file")

    // with all options (these are the default options explicitely declared)
    file.write("Hello my dear file")(codec = Codec.UTF8)

    // Convert several strings to the file
    // same options apply as for write
    file.writeStrings( "It costs" :: "one" :: "dollar" :: Nil)

    // Now all options
    file.writeStrings("It costs" :: "one" :: "dollar" :: Nil,
                    separator="||\n||")(codec = Codec.UTF8)
  }

15
스칼라 2.9 버전은 어떻습니까? :)
Eduardo Costa

scalax 프로젝트는 종료 된 것으로 보입니다 (2009 년 6 월 이후 커밋 없음). 이게 옳은 거니? scalax commit history
Rick-777

@Eduardo : 나는 scala-io 라이브러리를위한 새로운 장소 (Scala2.9를 위해 업데이트되었다 : github.com/jesseeichar/scala-io/issues/20 )에 대한 답변을 완료했다
VonC

10
이것이 실제로 Scala 2.10에 대한 현재 제안입니까? 스칼라 IO를 사용 하시겠습니까? 핵심 스칼라에는 아직 아무것도 없습니까?
Phil

2
scalax.io를 사용한 적이 없지만이 예제 라인에서 판단하면 API 디자인이 상당히 나쁜 것 같습니다. 하나의 인터페이스에서 문자 및 이진 데이터에 대한 혼합 방법은 거의 의미가 없으며 찾기 어려운 인코딩 버그로 이어질 가능성이 높습니다. java.io (Reader / Writer vs. InputStream / OutputStream)의 디자인이 훨씬 좋아 보입니다.
jcsahnwaldt Reinstate Monica

211

이것은 표준 스칼라에서 누락 된 기능 중 하나이므로 개인 라이브러리에 추가 할 때 유용합니다. (개인 라이브러리도 있어야합니다.) 코드는 다음과 같습니다.

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

다음과 같이 사용됩니다.

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

1
새로운 java.io.PrintWriter ()는 플랫폼 기본 인코딩을 사용하는데, 이는 아마도 결과 파일이 이식성이 좋지 않다는 것을 의미합니다. 예를 들어 나중에 전자 메일로 보낼 수있는 파일을 만들려면 인코딩을 지정할 수있는 PrintWriter 생성자를 사용해야합니다.
jcsahnwaldt Reinstate Monica

@JonaChristopherSahnwaldt-물론 특별한 경우 인코딩을 지정하고 싶을 수도 있습니다. 플랫폼의 기본값은 평균적으로 가장 합리적인 기본값입니다. with와 동일합니다 Source(기본적으로 기본 인코딩). 물론 일반적인 요구가 발견되면 enc: Option[String] = None매개 변수를 추가 할 수 있습니다 f.
Rex Kerr

6
@RexKerr-동의하지 않습니다. 거의 모든 경우에 인코딩을 지정해야합니다. 사람들이 인코딩을 이해하지 못하거나 생각하지 않기 때문에 발생하는 대부분의 인코딩 오류가 발생합니다. 너무 많은 API를 사용하면 기본값을 사용하므로 알 수 없습니다. 요즘 가장 합리적인 기본값은 아마도 UTF-8 일 것입니다. ASCII로 작성할 수있는 영어 및 기타 언어로만 작업 할 수 있습니다. 운이 좋다 나는 독일에 살고 있으며 기억해야 할 것보다 더 많은 움라우트를 고쳐야했습니다.
jcsahnwaldt Reinstate Monica

3
@JonaChristopherSahnwaldt-이것이 모든 사람이 항상 그것을 지정하도록 강요하지 않는 현명한 기본 인코딩을 갖는 이유입니다. 그러나 Mac에 있고 Java로 작성된 파일이 Mac OS Roman으로 인코딩되지 않았기 때문에 gobbledygook 인 경우 해를 끼치는 것보다 더 잘하고 있는지 확실하지 않습니다. 나는 그들이 문자 집합에 동의하지 않은 것이 플랫폼의 잘못이라고 생각합니다. 개별 개발자로서 문자열을 입력해도 문제가 해결되지는 않습니다. (UTF-8에 동의하는 모든 개발자는 기본적으로 사용할 수 있습니다.)
Rex Kerr

모든 깨진 움라우트를 고치기위한 @JonaChristopherSahnwaldt +10 망치를 사용하거나 구멍 펀치를 사용할 수 없습니까? 또는 그들은 이미 채울 필요가있는 구멍입니까, 아마도이 사람은 youtube.com/watch?v=E-eBBzWEpwE 를 도울 수 있습니다. 그러나 심각하게, ASCII의 영향은 세계에서 너무 손상되어 지정해야하며 동의해야합니다. 8
Davos

50

Rex Kerr의 답변과 비슷하지만 더 일반적입니다. 먼저 도우미 기능을 사용합니다.

/**
 * Used for reading/writing to database, files, etc.
 * Code From the book "Beginning Scala"
 * http://www.amazon.com/Beginning-Scala-David-Pollak/dp/1430219890
 */
def using[A <: {def close(): Unit}, B](param: A)(f: A => B): B =
try { f(param) } finally { param.close() }

그런 다음 이것을 다음과 같이 사용합니다.

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

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

기타


39
내가 틀리지 말고, 나는 당신의 코드를 좋아하고 매우 교육적이지만, 간단한 문제에 대한 그러한 구조를 많이 볼수록 오래된 "hello world"농담에 대해 더 많이 상기시킵니다. ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-) (+1 투표).
greenoldman

4
하나의 라이너를 작성하는 경우 전혀 중요하지 않습니다. 유지 관리 및 진화에 대한 지속적인 필요성이있는 중요한 프로그램을 작성하는 경우, 이러한 사고는 가장 빠르고 악의적 인 소프트웨어 품질 저하로 이어집니다.
랜달 슐츠

3
모든 사람이 실제로 어느 정도까지 "스칼라 눈"을해야 할 것입니다 -이 코드 예제는 "시작"스칼라에서 오는 보는 재미
asyncwait

asyncwait "시작"스칼라 ... 지금까지 가장 아이러니 한 제목, 참고 : 나는 책을 읽었고 ... 이제는 그것을 이해하기 시작했다. 나는 "초보자"전에 단계라고 생각했다. ........
user1050817

1
문제는 여기서 스칼라 트릭보다 적지 만, 장황하고 스타일이 좋지 않습니다. 나는 이것을 훨씬 더 읽기 쉽게 편집했다. 내 리 팩터 후 그것은 단지 4 줄입니다 (잘, IDE 줄 길이를 가진 4, 화면에 맞추기 위해 6을 사용했습니다). IMHO 그것은 지금 매우 좋은 답변입니다.
samthebest

38

간단한 답변 :

import java.io.File
import java.io.PrintWriter

def writeToFile(p: String, s: String): Unit = {
    val pw = new PrintWriter(new File(p))
    try pw.write(s) finally pw.close()
  }

1
@samthebest 라이브러리를 추가 할 수 import있습니까?
Daniel

1
java 7부터 java.nio.file을 대신 사용하십시오 .def writeToFile (file : String, stringToWrite : String) : Unit = {val writer = Files.newBufferedWriter (Paths.get (file)) writer.write (stringToWrite) writer.close ()}
E Shindler

20

다른 답변에 대한 수정 사항이 거부 된 경우 다른 답변을 제공합니다.

이것은 가장 간결하고 간단한 답변입니다 (가렛 홀과 유사)

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

이것은 Jus12와 비슷하지만 자세한 정보가없고 올바른 코드 스타일이 있습니다.

def using[A <: {def close(): Unit}, B](resource: A)(f: A => B): B =
  try f(resource) finally resource.close()

def writeToFile(path: String, data: String): Unit = 
  using(new FileWriter(path))(_.write(data))

def appendToFile(path: String, data: String): Unit =
  using(new PrintWriter(new FileWriter(path, true)))(_.println(data))

에 대한 중괄호 try finally나 람다 가 필요하지 않으며 자리 표시 자 구문의 사용법에 유의하십시오. 또한 더 나은 명명을 참고하십시오.


2
죄송하지만 코드는 상상할 수 있지만 implemented필수 구성 요소를 충족하지 못합니다 . 구현되지 않은 코드는 사용할 수 없습니다. 기본적으로 사용할 수없고 잘 알려지지 않았기 때문에 찾는 방법을 알려 주어야합니다.
Val

15

스칼라 컴파일러 라이브러리를 사용하는 간결한 원 라이너는 다음과 같습니다.

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

또는 Java 라이브러리를 사용하려는 경우 다음 해킹을 수행 할 수 있습니다.

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

수입품은 무엇입니까? 즉, 파일은 어디에서 오는가?
벤 허치슨

스칼라 컴파일러 라이브러리.
개렛 홀

3
더 이상 실행 가능한 (안 스칼라 2.11)
브렌트 파우스트

1
무슨 소리 야? scala.tools.nsc.io.File("/tmp/myFile.txt")스칼라 2.11.8에서 작동합니다.

1
scala.reflect.io.File에 있습니다
Keith Nordstrom

13

을 (를 String) 사용하여 저장 / 읽기위한 하나의 라이너 java.nio.

import java.nio.file.{Paths, Files, StandardOpenOption}
import java.nio.charset.{StandardCharsets}
import scala.collection.JavaConverters._

def write(filePath:String, contents:String) = {
  Files.write(Paths.get(filePath), contents.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE)
}

def read(filePath:String):String = {
  Files.readAllLines(Paths.get(filePath), StandardCharsets.UTF_8).asScala.mkString
}

큰 파일에는 적합하지 않지만 작업을 수행합니다.

일부 링크 :

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


대용량 파일에 적합하지 않은 이유는 무엇입니까?
Chetan Bhasin

2
@ChetanBhasin 아마도 파일로 스트리밍하는 대신 새로운 바이트 배열로 write복사 contents하기 때문에 contents혼자서 보다 두 배나 많은 메모리를 사용하기 때문 입니다.
Daniel Werner

10

불행히도 최고의 답변으로 Scala-IO는 죽었습니다. 타사 종속성을 사용하지 않으려면 OS-Lib 라이브러리 사용을 고려하십시오 . 파일, 경로 및 파일 시스템 작업이 매우 쉽습니다.

// Make sure working directory exists and is empty
val wd = os.pwd/"out"/"splash"
os.remove.all(wd)
os.makeDir.all(wd)

// Read/write files
os.write(wd/"file.txt", "hello")
os.read(wd/"file.txt") ==> "hello"

// Perform filesystem operations
os.copy(wd/"file.txt", wd/"copied.txt")
os.list(wd) ==> Seq(wd/"copied.txt", wd/"file.txt")

파일 쓰기 , 파일 추가 , 파일 덮어 쓰기 및 기타 여러 유용한 / 공통 작업을 위한 하나의 라이너가 있습니다.


이 업데이트에 감사드립니다. 공감. 더 많은 가시성을 위해 위의 답변을 참조했습니다.
VonC

7

내가 쓴 마이크로 라이브러리 : https://github.com/pathikrit/better-files

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

또는

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

여기도 있습니다-이 질문은 스칼라로 파일을 작성하는 방법을 인터넷 검색 할 때 가장 인기있는 질문 중 하나입니다. 이제 프로젝트가 커 졌으므로 답을 조금 확장하고 싶습니까?
asac

6

시작 Scala 2.13, 표준 라이브러리는 전용 자원 관리 유틸리티를 제공합니다 Using.

이 경우 파일에 쓰기 위해 확장 PrintWriter되거나 BufferedWriter확장되는 자원과 함께 사용할 수 있으며 AutoCloseable이후에 자원을 닫을 수 있습니다.

  • 예를 들어 java.ioapi를 사용하면 :

    import scala.util.Using, java.io.{PrintWriter, File}
    
    // val lines = List("hello", "world")
    Using(new PrintWriter(new File("file.txt"))) {
      writer => lines.foreach(writer.println)
    }
  • 또는 java.nioAPI로 :

    import scala.util.Using, java.nio.file.{Files, Paths}, java.nio.charset.Charset
    
    // val lines = List("hello", "world")
    Using(Files.newBufferedWriter(Paths.get("file.txt"), Charset.forName("UTF-8"))) {
      writer => lines.foreach(line => writer.write(line + "\n"))
    }

6

2019/9/01의 업데이트 :

  • Scala 2.13부터 scala.util을 선호합니다.
  • 버그를 수정 finally원래 삼키려 Exception에 의해 발생 try하는 경우 finally코드가 던져Exception

스칼라에서 파일을 쉽게 작성하는 방법에 대한이 모든 답변을 검토 한 결과 일부는 꽤 좋았으므로 세 가지 문제가있었습니다.

  1. 에서 Jus12의 대답 은 사용하는 도우미 메서드에 대한 무두질의 사용은 스칼라 / FP의 초보자를위한 비 명백하다
  2. 낮은 수준의 오류를 캡슐화해야합니다. scala.util.Try
  3. 스칼라 / FP 새로운 자바 개발자를 표시해야하는 방법을 제대로 둥지 따라 리소스 있도록 close방법은 역순으로 각 의존 자원에서 수행됩니다 - 참고 : 역순으로 따라 자원을 닫는 특히에 오류가 발생하는 의 거의 이해하지 요구 사항입니다 java.lang.AutoCloseable매우 치명적이고 버그를 찾기 어렵고 런타임 실패를 일으키는 경향이 있는 사양

시작하기 전에 내 목표는 간결하지 않습니다. Scala / FP 초보자, 일반적으로 Java에서 온 사람들이 쉽게 이해할 수 있도록합니다. 마지막에 모든 비트를 모아 간결성을 높이겠습니다.

먼저,이 using방법을 사용하려면 업데이트해야합니다 Try(다시 말해 간결함은 여기서 목표가 아닙니다). 이름이 tryUsingAutoCloseable다음 으로 변경됩니다 .

def tryUsingAutoCloseable[A <: AutoCloseable, R]
  (instantiateAutoCloseable: () => A) //parameter list 1
  (transfer: A => scala.util.Try[R])  //parameter list 2
: scala.util.Try[R] =
  Try(instantiateAutoCloseable())
    .flatMap(
      autoCloseable => {
        var optionExceptionTry: Option[Exception] = None
        try
          transfer(autoCloseable)
        catch {
          case exceptionTry: Exception =>
            optionExceptionTry = Some(exceptionTry)
            throw exceptionTry
        }
        finally
          try
            autoCloseable.close()
          catch {
            case exceptionFinally: Exception =>
              optionExceptionTry match {
                case Some(exceptionTry) =>
                  exceptionTry.addSuppressed(exceptionFinally)
                case None =>
                  throw exceptionFinally
              }
          }
      }
    )

위의 tryUsingAutoCloseable방법 의 시작은 일반적인 단일 매개 변수 목록 대신 두 개의 매개 변수 목록이있는 것처럼 보이기 때문에 혼란 스러울 수 있습니다. 이것을 카레라고합니다. 그리고 나는 카레가 어떻게 작동하는지 또는 때로는 유용한 곳을 자세히 설명하지 않을 것 입니다. 이 특정 문제 공간의 경우 작업에 적합한 도구라는 것이 밝혀졌습니다.

다음으로, 우리는 방법을 만들어야 tryPrintToFile하는을 작성 (또는 기존 덮어 쓰기) 할 File및 쓰기 List[String]. 에 FileWriter의해 캡슐화되는 a BufferedWriter를 사용하고 ,에 의해 캡슐화되는 a를 사용 PrintWriter합니다. 성능을 높이기 위해 기본값보다 훨씬 큰 기본 버퍼 크기 BufferedWriter가 정의 defaultBufferSize되어 값 65536이 지정됩니다.

코드는 다음과 같습니다 (그리고 간결함은 여기서 목표가 아닙니다).

val defaultBufferSize: Int = 65536

def tryPrintToFile(
  lines: List[String],
  location: java.io.File,
  bufferSize: Int = defaultBufferSize
): scala.util.Try[Unit] = {
  tryUsingAutoCloseable(() => new java.io.FileWriter(location)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
    fileWriter =>
      tryUsingAutoCloseable(() => new java.io.BufferedWriter(fileWriter, bufferSize)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
        bufferedWriter =>
          tryUsingAutoCloseable(() => new java.io.PrintWriter(bufferedWriter)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
            printWriter =>
              scala.util.Try(
                lines.foreach(line => printWriter.println(line))
              )
          }
      }
  }
}

위의 tryPrintToFile방법은 List[String]입력을 받아로 전송 한다는 점에서 유용 합니다 File. 이제 a tryWriteToFile를 가져 와서에 String쓰는 메소드를 만들어 봅시다 File.

코드는 다음과 같습니다 (여기서 간결함의 우선 순위를 추측하도록하겠습니다).

def tryWriteToFile(
  content: String,
  location: java.io.File,
  bufferSize: Int = defaultBufferSize
): scala.util.Try[Unit] = {
  tryUsingAutoCloseable(() => new java.io.FileWriter(location)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
    fileWriter =>
      tryUsingAutoCloseable(() => new java.io.BufferedWriter(fileWriter, bufferSize)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method
        bufferedWriter =>
          Try(bufferedWriter.write(content))
      }
  }
}

마지막으로 a의 내용을 페치 할 수있는 것이 유용 File합니다 String. 하지만 scala.io.Source쉽게의 내용을 얻기위한 편리한 방법을 제공 Fileclose방법은 사용해야합니다 Source기본 JVM 및 파일 시스템 핸들을 해제 할 수 있습니다. 이 작업을 수행하지 않으면 JVM GC (Garbage Collector)가 Source인스턴스 자체 를 해제 할 때까지 리소스가 해제되지 않습니다 . 그럼에도 불구 finalize하고 GC가 close리소스 에 메소드를 호출 한다는 보장은 약한 JVM 입니다. 즉 , 인스턴스를 작성하는 close것은 클라이언트의 책임과 마찬가지로 메소드 를 명시 적으로 호출하는 것은 클라이언트의 책임 close입니다.java.lang.AutoCloseable. 이를 위해를 처리하는 using 메소드의 두 번째 정의가 필요합니다 scala.io.Source.

이것에 대한 코드는 다음과 같습니다 (아직 간결하지는 않음).

def tryUsingSource[S <: scala.io.Source, R]
  (instantiateSource: () => S)
  (transfer: S => scala.util.Try[R])
: scala.util.Try[R] =
  Try(instantiateSource())
    .flatMap(
      source => {
        var optionExceptionTry: Option[Exception] = None
        try
          transfer(source)
        catch {
          case exceptionTry: Exception =>
            optionExceptionTry = Some(exceptionTry)
            throw exceptionTry
        }
        finally
          try
            source.close()
          catch {
            case exceptionFinally: Exception =>
              optionExceptionTry match {
                case Some(exceptionTry) =>
                  exceptionTry.addSuppressed(exceptionFinally)
                case None =>
                  throw exceptionFinally
              }
          }
      }
    )

그리고 다음은 슈퍼 간단한 라인 스트리밍 파일 리더에서 현재 사용되는 예제입니다 (현재 데이터베이스 출력에서 ​​탭으로 구분 된 파일을 읽는 데 사용됨).

def tryProcessSource(
    file: java.io.File
  , parseLine: (String, Int) => List[String] = (line, index) => List(line)
  , filterLine: (List[String], Int) => Boolean = (values, index) => true
  , retainValues: (List[String], Int) => List[String] = (values, index) => values
  , isFirstLineNotHeader: Boolean = false
): scala.util.Try[List[List[String]]] =
  tryUsingSource(scala.io.Source.fromFile(file)) {
    source =>
      scala.util.Try(
        ( for {
            (line, index) <-
              source.getLines().buffered.zipWithIndex
            values =
              parseLine(line, index)
            if (index == 0 && !isFirstLineNotHeader) || filterLine(values, index)
            retainedValues =
              retainValues(values, index)
          } yield retainedValues
        ).toList //must explicitly use toList due to the source.close which will
                 //occur immediately following execution of this anonymous function
      )
  )

위 함수업데이트 된 버전은 다르지만 관련된 StackOverflow 질문에 대한 답변으로 제공되었습니다 .


이제 가져 오기 한 가져 오기와 함께 가져옵니다 (Eclipse ScalaIDE 및 IntelliJ Scala 플러그인 모두에있는 Scala Worksheet에 붙여 넣기가 훨씬 쉬워 져서 텍스트 편집기로보다 쉽게 ​​검사 할 수 있도록 데스크탑에 출력을 덤프하는 것이 더 쉬워졌습니다). 이 코드는 다음과 같습니다 (간결성이 증가 함).

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

스칼라 / FP 초보자로서 위의 지식과 솔루션을 얻기 위해 많은 시간을 쏟았습니다 (주로 머리를 긁적 좌절감으로). 이것이 다른 스칼라 / FP 초보자들이이 특정 학습 혹을 더 빨리 극복하는 데 도움이되기를 바랍니다.


2
놀라운 업데이트. 유일한 문제는 이제 100 줄의 코드를 사용할 수 있다는 것입니다 try-catch-finally. 여전히 당신의 열정을 사랑하십시오.
관찰자

1
@Observer 나는 그것이 부정확 한 진술이라고 주장한다. 내가 설명하는 패턴은 실제로 AutoCloseables를 올바르게 처리하기 위해 클라이언트 가 작성 해야하는 상용구의 양을 줄이면서 scala.util.Try를 사용하는 Scala 관용적 FP 패턴을 활성화하는 것입니다. try / catch / finally 블록을 수동으로 작성하여 내가 가진 동일한 효과를 얻으려고하면 상상하는 것보다 훨씬 더 많은 상용구가 생길 것입니다. 따라서 모든 상용구를 100 라인의 스칼라 기능으로 밀어 넣을 때 가독성이 상당히 높습니다.
chaotic3quilibrium

1
어떤 식 으로든 불쾌하게 들리면 죄송합니다. 그럼에도 불구하고, 내 요점은 그러한 양의 코드가 필요하지 않다는 것입니다. 왜냐하면 훨씬 더 단순하게 비 기능적 접근 방식을 통해 달성 할 수 있기 때문입니다. 개인적으로, 나는 몇 가지 추가 점검으로 최종 시험을 씁니다. 더 짧아요 래퍼를 사용하고 싶다면 ApacheUtils가 모든 더러운 작업을 수행해야합니다. 또한 모든 표준 리더 / 라이터는 기본 스트림을 닫으므로 멀티 랩이 필요하지 않습니다. 추신 : 귀하의 노력을 지원하기 위해 투표를 마이너스 1에서 플러스 1로 변경했습니다. 그러니 제발, 나쁜 의도로 나를 의심하지 마십시오.
관찰자

공격이 없습니다.
chaotic3quilibrium

1
당신의 관점을 이해합니다. 토론 주셔서 감사합니다, 나는 그것에 대해 조금 생각해야합니다. 좋은 하루 보내세요!
관찰자

3

다음은 scalaz-stream을 사용하여 파일에 일부 행을 쓰는 예입니다 .

import scalaz._
import scalaz.stream._

def writeLinesToFile(lines: Seq[String], file: String): Task[Unit] =
  Process(lines: _*)              // Process that enumerates the lines
    .flatMap(Process(_, "\n"))    // Add a newline after each line
    .pipe(text.utf8Encode)        // Encode as UTF-8
    .to(io.fileChunkW(fileName))  // Buffered write to the file
    .runLog[Task, Unit]           // Get this computation as a Task
    .map(_ => ())                 // Discard the result

writeLinesToFile(Seq("one", "two"), "file.txt").run

1

samthebest와 그 앞에 기여한 사람들을 능가하기 위해 나는 이름과 간결성을 개선했습니다.

  def using[A <: {def close() : Unit}, B](resource: A)(f: A => B): B =
    try f(resource) finally resource.close()

  def writeStringToFile(file: File, data: String, appending: Boolean = false) =
    using(new FileWriter(file, appending))(_.write(data))

이것은 반사에 의존하는 "오리 타이핑"을 사용합니다. 많은 맥락에서, 성찰에 의존하는 것은 시작이 아닙니다.
chaotic3quilibrium

1

오류 처리와 함께 종속성 없음

  • 표준 라이브러리의 메소드를 독점적으로 사용
  • 필요한 경우 파일의 디렉토리를 작성합니다
  • 용도 Either오류 처리에 대한

암호

def write(destinationFile: Path, fileContent: String): Either[Exception, Path] =
  write(destinationFile, fileContent.getBytes(StandardCharsets.UTF_8))

def write(destinationFile: Path, fileContent: Array[Byte]): Either[Exception, Path] =
  try {
    Files.createDirectories(destinationFile.getParent)
    // Return the path to the destinationFile if the write is successful
    Right(Files.write(destinationFile, fileContent))
  } catch {
    case exception: Exception => Left(exception)
  }

용법

val filePath = Paths.get("./testDir/file.txt")

write(filePath , "A test") match {
  case Right(pathToWrittenFile) => println(s"Successfully wrote to $pathToWrittenFile")
  case Left(exception) => println(s"Could not write to $filePath. Exception: $exception")
}

1

2019 년 업데이트 :

요약-Java NIO (또는 비동기 용 NIO.2)는 여전히 Scala에서 지원되는 가장 포괄적 인 파일 처리 솔루션입니다. 다음 코드는 텍스트를 작성하여 새 파일에 씁니다.

import java.io.{BufferedOutputStream, OutputStream}
import java.nio.file.{Files, Paths}

val testFile1 = Paths.get("yourNewFile.txt")
val s1 = "text to insert in file".getBytes()

val out1: OutputStream = new BufferedOutputStream(
  Files.newOutputStream(testFile1))

try {
  out1.write(s1, 0, s1.length)
} catch {
  case _ => println("Exception thrown during file writing")
} finally {
  out1.close()
}
  1. Java 라이브러리 가져 오기 : IO 및 NIO
  2. 만들기 Path선택한 파일 이름 객체
  3. 파일에 삽입하려는 텍스트를 바이트 배열로 변환
  4. 파일을 스트림으로 가져옵니다. OutputStream
  5. 바이트 배열을 출력 스트림의 write함수에 전달
  6. 스트림을 닫습니다

1

이 답변 과 마찬가지로 fs2(버전 1.0.4) 의 예는 다음과 같습니다.

import cats.effect._

import fs2._
import fs2.io

import java.nio.file._

import scala.concurrent.ExecutionContext
import scala.language.higherKinds
import cats.syntax.functor._

object ScalaApp extends IOApp {

  def write[T[_]](p: Path, s: String)
                 (implicit F: ConcurrentEffect[T], cs: ContextShift[T]): T[Unit] = {
    Stream(s)
      .covary[T]
      .through(text.utf8Encode)
      .through(
        io.file.writeAll(
          p,
          scala.concurrent.ExecutionContext.global,
          Seq(StandardOpenOption.CREATE)
        )
      )
      .compile
      .drain
  }


  def run(args: List[String]): IO[ExitCode] = {

    implicit val executionContext: ExecutionContext =
      scala.concurrent.ExecutionContext.Implicits.global

    implicit val contextShift: ContextShift[IO] =
      IO.contextShift(executionContext)

    val outputFile: Path = Paths.get("output.txt")

    write[IO](outputFile, "Hello world\n").as(ExitCode.Success)

  }
}

0

이 줄은 Array 또는 String에서 파일을 쓰는 데 도움이됩니다.

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

0

어쨌든 프로젝트에 Akka Streams가있는 경우 하나의 라이너를 제공합니다.

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

Akka Docs> 스트리밍 파일 IO

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.