스칼라 : 한 문장으로 파일에 문자열 쓰기


144

스칼라에서 파일을 읽으려면

Source.fromFile("file.txt").mkString

문자열을 파일에 쓰는 동등하고 간결한 방법이 있습니까?

대부분의 언어는 그런 것을 지원합니다. 내가 가장 좋아하는 것은 Groovy입니다.

def f = new File("file.txt")
// Read
def s = f.text
// Write
f.text = "file contents"

한 줄에서 짧은 코드 페이지에 이르는 프로그램에 코드를 사용하고 싶습니다. 자신의 라이브러리를 사용해야한다는 것은 여기서 의미가 없습니다. 현대 언어로 파일에 무언가를 편리하게 쓸 수 있기를 기대합니다.

이와 비슷한 게시물이 있지만 정확한 질문에 답하지 않거나 이전 스칼라 버전에 중점을 둡니다.

예를 들면 다음과 같습니다.


참조 질문을. 가장 높은 등급의 답변에 동의합니다. 개인 라이브러리를 갖는 것이 좋습니다.
ffriend

2
이 경우, 나는 자신의 개인 라이브러리를 작성해야한다는 것에 동의하지 않습니다. 일반적으로 임시 작업을 수행하기 위해 작은 프로그램을 작성할 때 (단일 페이지 스칼라 스크립트 또는 REPL로 작성하고 싶을 수도 있습니다). 그런 다음 개인 라이브러리에 액세스하면 고통이됩니다.
smartnut007

기본적 으로이 시점에서 scala 2.9에는 아무것도없는 것처럼 보입니다. 이 질문을 열어 두어야하는지 잘 모르겠습니다.
smartnut007

1
스칼라 소스 코드에서 java.io를 검색하면 출력 작업, 특히 PrintWriter와 같은 경우가 많지 않습니다. 따라서 Scala-IO 라이브러리가 Scala의 공식적인 부분이 될 때까지 우리는 패러다임으로 표시된 것처럼 순수한 Java를 사용해야합니다.
PhiLho

예, prob에는 jdk7 IO 개선과 호환되는 scala-io도 필요합니다.
smartnut007

답변:


77

간결한 한 줄 :

import java.io.PrintWriter
new PrintWriter("filename") { write("file contents"); close }

14
이 방법은 멋지게 보이지만 예외 안전이나 인코딩 안전은 아닙니다. 예외가에서 발생하는 경우 write(), close호출되지 않을 것이며, 파일이 닫히지 않습니다. PrintWriter또한 기본 시스템 인코딩을 사용하므로 이식성 이 매우 나쁩니다. 그리고 마지막으로,이 접근법은이 라인을 위해 특별히 별도의 클래스를 생성합니다 (그러나 스칼라가 간단한 코드에서도 이미 수많은 클래스를 생성한다는 점에서 그 자체로는 단점이 아닙니다).
Vladimir Matveev

위의 의견에 따르면, 이것은 하나의 라이너이지만 안전하지 않습니다. : 당신이 위치의 주위에 더 많은 옵션을 가지고 및 / 또는 입력을 버퍼링하는 동안 더 안전하려면, 난 그냥 비슷한 스레드에 게시 된 답변을 참조 stackoverflow.com/a/34277491/501113
chaotic3quilibrium

2
임시 디버그 로깅을 위해new PrintWriter(tmpFile) { try {write(debugmsg)} finally {close} }
Randall Whitman

문체는 아마도 더 나은 사용에있어 close(), 나는 생각
케이시

이것은 두 new java.io.PrintWriter("/tmp/hello") { write("file contents"); close }
줄이며

163

아무도 NIO.2 작업을 제안하지 않은 것은 이상합니다 (Java 7부터 사용 가능).

import java.nio.file.{Paths, Files}
import java.nio.charset.StandardCharsets

Files.write(Paths.get("file.txt"), "file contents".getBytes(StandardCharsets.UTF_8))

나는 이것이 가장 간단하고 가장 쉽고 관용적 인 방법이라고 생각하며 Java 자체의 종속성이 필요하지 않습니다.


어떤 이유로 든 개행 문자를 무시합니다.
Kakaji

@ 카카 지 씨, 좀 더 자세히 설명해 주시겠습니까? 방금 줄 바꿈이있는 문자열로 테스트했으며 완벽하게 작동합니다. 아무것도 필터링 할 수 없습니다-Files.write ()는 내용을 분석하지 않고 바이트 배열을 blob으로 작성합니다. 결국, 일부 이진 데이터에서 0x0d 바이트는 줄 바꿈 이외의 중요한 의미를 가질 수 있습니다.
Vladimir Matveev 7

4
추가 참고 사항 : File이있는 경우 '.toPath'만으로 첫 번째 매개 변수를 얻습니다.
akauppi

1
이것은 명백한 CharSets 선택으로 인해 더 산업 등급이지만의 단순성 (및 하나의 라이너 ..)이 부족합니다 reflect.io.File.writeAll(contents). 왜? 두 개의 import 문을 포함하면 세 줄입니다. 어쩌면 IDE가 자동으로 수행하지만 REPL에서는 쉽지 않습니다.
javadba

3
우리가 JVM에있는 @javadba, 가져 오기는 특히 '줄'로 간주되지 않습니다. 대체 방법은 거의 항상 새로운 라이브러리 종속성을 추가하기 때문입니다. 어쨌든, Files.write또한 java.lang.Iterable두 번째 인수로 a 를 받아들이고, Iterable변환기를 사용하여 Scala로부터 , 즉 거의 모든 컬렉션 유형을 얻을 수 있습니다 :import java.nio.file.{Files, Paths}; import scala.collection.JavaConverters.asJavaIterableConverter; val output = List("1", "2", "3"); Files.write(Paths.get("output.txt"), output.asJava)
Yawar

83

reflect.io.file을 사용하는 간결한 1 라이너는 Scala 2.12와 함께 작동합니다.

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

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

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

1
+1 잘 작동합니다. Some/ foreach콤보는 비트 펑키이지만, 결국 어떤 시도 / 캐치의 이익을 / 함께 제공됩니다.
브렌트 파우스트

3
쓰기에서 예외가 발생하면 예외에서 복구하고 파일을 다시 읽거나 쓰려는 경우 파일을 닫을 수 있습니다. 다행스럽게도 scala는이 작업을 수행하기 위해 하나의 라이너를 제공합니다.
개렛 홀

25
scala.tools.nsc.io 패키지는 공용 API의 일부가 아니지만 컴파일러에서 사용되므로 권장되지 않습니다.
Giovanni Botta

3
Some/ foreach많은 사람들은 생산에 해커가 발생 읽을 수없는 코드에 대한 스칼라를 싫어하는 이유를 해킹 정확히이다.
Erik Kaplun

3
scala.tootls.nsc.io.File의 별칭입니다 reflect.io.File. 여전히 내부 API이지만 조금 더 짧습니다.
kostja

41

Groovy 구문이 마음에 들면 Pimp-My-Library 디자인 패턴을 사용하여 Scala로 가져올 수 있습니다.

import java.io._
import scala.io._

class RichFile( file: File ) {

  def text = Source.fromFile( file )(Codec.UTF8).mkString

  def text_=( s: String ) {
    val out = new PrintWriter( file , "UTF-8")
    try{ out.print( s ) }
    finally{ out.close }
  }
}

object RichFile {

  implicit def enrichFile( file: File ) = new RichFile( file )

}

예상대로 작동합니다.

scala> import RichFile.enrichFile
import RichFile.enrichFile

scala> val f = new File("/tmp/example.txt")
f: java.io.File = /tmp/example.txt

scala> f.text = "hello world"

scala> f.text
res1: String = 
"hello world

2
Source.fromFile 반환 인스턴스에서 close를 호출하지 마십시오. 즉 리소스가 GCed (Garbage Collected)가 될 때까지 닫히지 않습니다. 그리고 PrintWriter는 버퍼링되지 않으므로 8 바이트의 작은 JVM 기본 버퍼를 사용하므로 잠재적으로 IO가 느려집니다. 난 그냥 비슷한 스레드에서 답변을 만든 이들 문제를 다룬다 : stackoverflow.com/a/34277491/501113
chaotic3quilibrium

1
네 말이 맞아 그러나 내가 여기에 준 솔루션은 작은 IO를 가진 단기 프로그램에 적합합니다. 서버 프로세스 또는 대용량 데이터 (일반적으로 500MB 이상)에는 권장하지 않습니다.
패러다임

23
import sys.process._
"echo hello world" #> new java.io.File("/tmp/example.txt") !

REPL에서는 작동하지 않습니다. 오류 메시지는 없지만 /tmp/example.txt를 보면 없습니다.
사용자가 알 수 없음

@user unknown, '!'을 (를) 누락하여 죄송합니다. 명령의 끝에서 지금 수정되었습니다.
xiefei

훌륭한! 이제 작동하므로 이유를 알고 싶습니다. 무엇입니까 #>, 무엇을 !합니까?
사용자가 알 수 없음

10
이 솔루션은 이식성 이 없습니다 ! * nix 시스템에서만 작동합니다.
Giovanni Botta

2
임의의 문자열을 작성하는 데는 작동하지 않습니다. 명령 행 echo도구에 인수로 전달할 수있는 짧은 문자열에 대해서만 작동합니다 .
Rich

15

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

file.write("Hi!")

또는

file << "Hi!"

3
도서관을 사랑하십시오! 이 질문은 스칼라로 파일을 작성하는 방법을 인터넷 검색 할 때 가장 많이 발생하는 질문 중 하나입니다. 이제 프로젝트가 커 졌으므로 답을 조금 확장하고 싶습니까?
asac

12

Apache File Utils를 쉽게 사용할 수 있습니다 . 기능을보십시오 writeStringToFile. 우리는 프로젝트에서이 라이브러리를 사용합니다.


3
나는 항상 그것을 사용합니다. 질문을주의 깊게 읽으면 라이브러리를 사용하고 싶지 않은 이유를 이미 알았습니다.
smartnut007

라이브러리를 사용하지 않고 읽기 / 쓰기 중 예외를 처리하고 Java 라이브러리에서 제공하는
최소

7

하나는이 형식을 가지고 있으며 간결하고 기본 라이브러리는 아름답게 작성되었습니다 (소스 코드 참조).

import scalax.io.Codec
import scalax.io.JavaConverters._

implicit val codec = Codec.UTF8

new java.io.File("hi-file.txt").asOutput.write("I'm a hi file! ... Really!")

7

이것은 간결합니다.

scala> import java.io._
import java.io._

scala> val w = new BufferedWriter(new FileWriter("output.txt"))
w: java.io.BufferedWriter = java.io.BufferedWriter@44ba4f

scala> w.write("Alice\r\nBob\r\nCharlie\r\n")

scala> w.close()

4
공정한 충분하지만,이 "간결에 충분한", "하나의 문"으로 분류하지 않습니다 : P
에릭 Kaplun

이 코드는 Java의 인식 된 많은 문제를 요약합니다. 불행히도 스칼라는 IO를 충분히 중요하게 생각하지 않으므로 표준 라이브러리에는 IO가 포함되지 않습니다.
Chris

이 답변은 새로운 FileWriter의 고아 리소스 문제를 숨 깁니다. 새 FileWriter가 성공했지만 새 BufferedWriter가 실패하면 FileWriter 인스턴스는 다시 표시되지 않으며 GCed (Garbage Collected)까지 계속 열려 있고 닫히지 않을 수 있습니다 (JVM에서 최종 작업을 보장하는 방식으로 인해). : 나는 비슷한 이러한 문제를 해결 질문에 대한 답을 작성했습니다 stackoverflow.com/a/34277491/501113
chaotic3quilibrium

2

Java 및 Scala 라이브러리를 혼합하여이 작업을 수행 할 수 있습니다. 문자 인코딩을 완전히 제어 할 수 있습니다. 그러나 불행히도 파일 핸들이 제대로 닫히지 않습니다.

scala> import java.io.ByteArrayInputStream
import java.io.ByteArrayInputStream

scala> import java.io.FileOutputStream
import java.io.FileOutputStream

scala> BasicIO.transferFully(new ByteArrayInputStream("test".getBytes("UTF-8")), new FileOutputStream("test.txt"))

코드에 고아 리소스 인스턴스 문제가 있습니다. 호출하기 전에 인스턴스를 캡처하지 않기 때문에 호출하는 메소드에 매개 변수가 전달되기 전에 예외가 발생하면 성공적으로 인스턴스화 된 자원이 GCed (Garbage Collected)까지 닫히지 않으며 심지어 GC가 작동하는 방식으로 인한 것이 아닐 수 있습니다. : 나는 비슷한 이러한 문제를 해결 질문에 대한 답을 작성했습니다 stackoverflow.com/a/34277491/501113
chaotic3quilibrium

1
당신이 옳고 해결책이 아주 좋습니다. 그러나 여기서 요구 사항은 한 줄로 수행하는 것이 었습니다. 그리고 당신 이주의 깊게 읽을 때, 나는이 요구 사항과 접근 방식에 따른 제한으로 내 대답에서 리소스 누출을 언급했습니다. 귀하의 솔루션은 훌륭하지만 한 줄 요구 사항과 일치하지 않습니다.
stefan.schwetschke

2

나는 그것이 한 줄이 아니라는 것을 알고 있지만 내가 알 수있는 한 안전 문제를 해결합니다.

// This possibly creates a FileWriter, but maybe not
val writer = Try(new FileWriter(new File("filename")))
// If it did, we use it to write the data and return it again
// If it didn't we skip the map and print the exception and return the original, just in-case it was actually .write() that failed
// Then we close the file
writer.map(w => {w.write("data"); w}).recoverWith{case e => {e.printStackTrace(); writer}}.map(_.close)

예외 처리에 신경 쓰지 않았다면 쓸 수 있습니다.

writer.map(w => {w.writer("data"); w}).recoverWith{case _ => writer}.map(_.close)

1

업데이트 : 나는 여기에서 상세하게 설명 한보 다 효과적인 솔루션을 만들었습니다 : https://stackoverflow.com/a/34277491/501113

Eclipse 용 Scala IDE의 Scala Worksheet에서 점점 더 많은 작업을하고 있습니다 (IntelJIDEA에 동등한 것이 있다고 생각합니다). 어쨌든, "출력이 컷오프 한계를 초과합니다."라는 메시지가 표시 될 때 일부 내용을 출력하기 위해 하나의 라이너를 사용할 수 있어야합니다. 특히 스칼라 컬렉션에서 중요한 일을하고 있다면 메시지.

나는 이것을 단순화하기 위해 각각의 새로운 스칼라 워크 시트의 상단에 삽입 한 1 개의 라이너를 생각해 냈습니다. 당신이 stickler이고 기술적으로 두 줄이라는 것을 알게되면, 그것은이 포럼에서 더 읽기 쉽게 만들어야합니다. 내 Scala Worksheet에서 한 줄입니다.

def printToFile(content: String, location: String = "C:/Users/jtdoe/Desktop/WorkSheet.txt") =
  Some(new java.io.PrintWriter(location)).foreach{f => try{f.write(content)}finally{f.close}}

사용법은 간단합니다.

printToFile("A fancy test string\ncontaining newlines\nOMG!\n")

이를 통해 기본값 이외의 추가 파일을 원할 경우 파일 이름을 선택적으로 제공 할 수 있습니다 (메소드가 호출 될 때마다 파일을 완전히 덮어 씁니다).

두 번째 사용법은 다음과 같습니다.

printToFile("A fancy test string\ncontaining newlines\nOMG!\n", "C:/Users/jtdoe/Desktop/WorkSheet.txt")

즐겨!


1

암모나이트 작전 사용 라이브러리를 . 구문은 매우 작지만 라이브러리의 너비는 bash와 같은 쉘 스크립팅 언어로 그러한 작업을 시도 할 때 예상되는 것만 큼 넓습니다.

링크 된 페이지에는 라이브러리로 수행 할 수있는 수많은 작업이 표시되어 있지만이 질문에 대답하기 위해 파일에 쓰는 예입니다.

import ammonite.ops._
write(pwd/'"file.txt", "file contents")

-1

세미콜론의 마법을 통해 원하는 것을 하나의 라이너로 만들 수 있습니다.

import java.io.PrintWriter
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.charset.StandardCharsets
import java.nio.file.StandardOpenOption
val outfile = java.io.File.createTempFile("", "").getPath
val outstream = new PrintWriter(Files.newBufferedWriter(Paths.get(outfile)
  , StandardCharsets.UTF_8
  , StandardOpenOption.WRITE)); outstream.println("content"); outstream.flush(); outstream.close()

여기에는 논쟁이 없습니다. 나는 어떤 API가 내 인생의 일부를 플러시 해야하는지 기억하지 않기로 결정했기 때문에 항상 그렇게합니다.
이온 프리먼
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.