스칼라에서 전체 파일을 읽습니까?


312

스칼라에서 전체 파일을 메모리로 읽는 간단하고 표준적인 방법은 무엇입니까? (이상적으로 문자 인코딩을 제어 할 수 있습니다.)

내가 생각해 낼 수있는 최선은 다음과 같습니다.

scala.io.Source.fromPath("file.txt").getLines.reduceLeft(_+_)

또는 Java의 신형 관용구 중 하나를 사용해야합니까 (외부 라이브러리를 사용하지 않고) 가장 좋은 것은 다음과 같습니다.

import java.util.Scanner
import java.io.File
new Scanner(new File("file.txt")).useDelimiter("\\Z").next()

메일 링리스트 토론을 읽음으로써 scala.io.Source가 표준 I / O 라이브러리 여야한다는 것은 확실하지 않습니다. 의도 된 목적이 무엇인지 정확히 이해하지 못합니다.

... 나는 간단하고 기억하기 쉬운 것을 원합니다. 예를 들어, 이러한 언어에서는 관용구를 잊어 버리기가 매우 어렵습니다 ...

Ruby    open("file.txt").read
Ruby    File.read("file.txt")
Python  open("file.txt").read()

12
올바른 도구를 알고 있다면 Java는 그렇게 나쁘지 않습니다. import org.apache.commons.io.FileUtils; FileUtils.readFileToString (새 파일 ( "file.txt", "UTF-8")
smartnut007

25
이 의견은 언어 설계의 요점을 놓치고 있습니다. 따라서 정확히 수행하려는 작업에 대해 간단한 라이브러리 함수를 사용할 수있는 모든 언어는 함수 호출 구문만큼 좋습니다. 무한하고 100 % 기억 된 라이브러리가 주어지면 모든 프로그램은 단일 함수 호출로 구현됩니다. 프로그래밍 결과는 특정 결과를 달성하기 위해 더 적은 수의 프리 팹 구성 요소가 필요한 경우에 좋습니다.
Chris Mountford

답변:


429
val lines = scala.io.Source.fromFile("file.txt").mkString

그건 그렇고, " scala."는 항상 범위 내에 있기 때문에 실제로는 필요하지 않으며, 물론 io의 내용을 전체적으로 또는 부분적으로 가져오고 "io"를 앞에 두지 않아도됩니다. 너무.

그러나 위의 파일은 열린 상태로 유지됩니다. 문제를 피하려면 다음과 같이 닫아야합니다.

val source = scala.io.Source.fromFile("file.txt")
val lines = try source.mkString finally source.close()

위의 코드의 또 다른 문제는 구현 특성으로 인해 끔찍하다는 것입니다. 더 큰 파일의 경우 다음을 사용해야합니다.

source.getLines mkString "\n"

48
나는 파티에 너무 늦었지만 사람들이 트렁크에서 "io.File ("/ etc / passwd "). slurp"를 수행 할 수 있다는 것을 모르고 싶었다.
psp

28
@extempore 정말 고맙다고 생각한다면 정말 죄송합니다. 스칼라 언어에 대한 귀하의 지원에 깊이 감사 드리며, 여러분이 개인적으로 제기 한 문제를 조사 할 때마다, 내가 가진 문제에 대한 해결책을 제안하거나, 나에게 무언가를 설명했습니다. 그렇다면 scala.io를 적절한 수준으로 바꾸어 주셔서 감사합니다. 앞으로도 감사의 말을 전할 것이지만, 여전히 이름이 싫습니다. 죄송합니다.
Daniel C. Sobral

49
"slurp"는 몇 년 동안 Perl에서 한 번에 전체 파일을 읽는 이름입니다. Perl은 C 언어의 언어보다 더 내장적이고 비공식적 인 명명 전통을 가지고 있는데, 일부 언어는 불쾌감을 줄 수 있지만,이 경우에는 적합하다고 생각합니다. 추악한 연습을위한 못생긴 단어입니다. 당신이 slurp ()을 할 때, 당신은 그것을 입력해야하기 때문에 당신이 나쁜 일을하고 있음을 알고 있습니다.
Marcus Downing

15
File.read ()는 더 좋은 이름이며 Ruby 및 Python 외에도 일관성이 있습니다.
Brendan OConnor

26
@extempore : 사람들이 혐오감을 막을 수는 없습니다. 그것은 그대로입니다. 어떤 사람들은 당신이 한 모든 선택을 좋아하지 않는다고 귀찮게해서는 안됩니다. 그것은 단지 인생입니다, 당신은 모두를 기쁘게 할 수 없습니다 :)
Alex Baranosky

58

Daniel의 솔루션을 확장하기 위해 파일 조작이 필요한 파일에 다음 가져 오기를 삽입하여 작업을 크게 줄일 수 있습니다.

import scala.io.Source._

이를 통해 이제 다음을 수행 할 수 있습니다.

val lines = fromFile("file.txt").getLines

전체 파일을 하나의 파일로 읽는 것에주의해야합니다 String. 그것은 당신이 생각하는 것보다 더 빨리 그리고 어려워하는 매우 나쁜 습관입니다. 이 getLines메소드는 유형의 값을 리턴합니다 Iterator[String]. 효과적으로 파일에 게으른 커서로 메모리 부족 위험없이 필요한 데이터 만 검사 할 수 있습니다.

아, 그리고 당신의 암묵적 인 질문에 대답하기 위해 Source: 예, 표준 I / O 라이브러리입니다. 대부분의 코드는 java.io하위 수준의 인터페이스와 기존 프레임 워크와의 호환성으로 인해 결국 사용 되지만 Source, 특히 간단한 파일 조작을 위해서는 선택이 가능한 모든 코드를 사용해야합니다 .


확인. Source에 대한 부정적인 인상에 대한 이야기가 있습니다. 한때는 메모리와 맞지 않는 매우 큰 파일을 가진 지금과 다른 상황이었습니다. 소스를 사용하면 프로그램이 중단되었습니다. 그것은 모든 것을 한 번에 읽으려고 노력하는 것으로 밝혀졌습니다.
Brendan OConnor

7
소스는 전체 파일을 메모리로 읽지 않아야합니다. getLines 다음에 toList를 사용하거나 콜렉션을 생성하는 다른 메소드를 사용하면 모든 것이 메모리에 저장됩니다. 이제 Source는 신중하게 생각한 라이브러리가 아니라 작업을 수행하기위한 해킹 입니다. Scala 2.8에서는 개선 될 것이지만, 스칼라 커뮤니티가 좋은 I / O API를 정의하는 데 적극적으로 참여할 수있는 기회가 있습니다.
Daniel C. Sobral

36
// for file with utf-8 encoding
val lines = scala.io.Source.fromFile("file.txt", "utf-8").getLines.mkString

6
원래 답변에 "getLines"를 추가하면 모든 줄 바꿈이 제거됩니다. "Source.fromFile ("file.txt ","utf-8 "). mkString"이어야합니다.
Joe23

9
Daniel C. Sobral의 답변에서 내 의견을 참조하십시오.이 사용으로 인해 Source 인스턴스가 닫히지 않으므로 Scala가 파일에 대한 잠금을 유지할 수 있습니다.
djb

26

(편집 : 이것은 scala 2.9에서는 작동하지 않으며 2.8에서는 작동하지 않습니다)

트렁크 사용 :

scala> io.File("/etc/passwd").slurp
res0: String = 
##
# User Database
# 
... etc

14
" slurp"? 우리는 분명하고 직관적 인 이름을 버렸습니까? 문제 slurp는 적어도 영어를 모국어로 사용하는 사람에게는 사실 이해 될 수 있지만 처음에는 생각하지 않을 것입니다!
Daniel C. Sobral

5
이 질문 / 답변에서 우연히 발견되었습니다. File더 이상 2.8.0에 있지 않습니까?
huynhjl

4
으악 소리가 좋아. :) 나는 그것을 기대하지 않지만 화면에 출력되는 이름이 'print'라고 기대하지는 않았습니다. slurp환상적이다! :) 환상적 이었습니까? 나는 그것을 찾지 못한다. ; (
사용자가 알 수 없음

5
scala-2.10.0에서 패키지 이름은 scala.reflect.io.File이며이 "파일"에 대한 질문입니다. 예, 왜이 파일이 "실험적"으로 표시되어 있습니까? 안전 해요? 파일 시스템에 대한 잠금을 해제합니까?
VasiliNovikov

4
slurp은 perl에서 유래 한이 목적에 대한 오랜 역사를 가지고 있습니다.
Chris Mountford

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

new String(Files.readAllBytes(Paths.get("file.txt")), UTF_8)

문자 인코딩을 제어하고 정리할 리소스가 없습니다. 또한 최적화 될 수 있습니다 (예 : Files.readAllBytes파일 크기에 적합한 바이트 배열 할당).


7

Source.fromFile에 문제가 있다고 들었습니다. 개인적으로 Source.fromFile을 사용하여 큰 파일을 여는 데 문제가 있었고 Java InputStreams를 사용해야했습니다.

또 다른 흥미로운 해결책은 scalax를 사용하는 것입니다. 다음은 ManagedResource를 사용하여 scalax 헬퍼가있는 파일을 여는 로그 파일을 여는 주석이 달린 코드의 예입니다. http://pastie.org/pastes/420714


6

scala.io.Source에서 getLines ()를 사용하면 줄 종결 자에 사용 된 문자 (\ n, \ r, \ r \ n 등)가 삭제됩니다.

다음은 문자별로 유지해야하며 과도한 문자열 연결을 수행하지 않습니다 (성능 문제).

def fileToString(file: File, encoding: String) = {
  val inStream = new FileInputStream(file)
  val outStream = new ByteArrayOutputStream
  try {
    var reading = true
    while ( reading ) {
      inStream.read() match {
        case -1 => reading = false
        case c => outStream.write(c)
      }
    }
    outStream.flush()
  }
  finally {
    inStream.close()
  }
  new String(outStream.toByteArray(), encoding)
}

6

하나 더 : https://github.com/pathikrit/better-files#streams-and-codecs

내용을 메모리에로드하지 않고 파일을 슬러 핑하는 다양한 방법 :

val bytes  : Iterator[Byte]            = file.bytes
val chars  : Iterator[Char]            = file.chars
val lines  : Iterator[String]          = file.lines
val source : scala.io.BufferedSource   = file.content 

읽기 / 쓰기를 수행하는 모든 작업에 자체 코덱을 제공 할 수도 있습니다 (제공하지 않으면 scala.io.Codec.default라고 가정).

val content: String = file.contentAsString  // default codec
// custom codec:
import scala.io.Codec
file.contentAsString(Codec.ISO8859)
//or
import scala.io.Codec.string2codec
file.write("hello world")(codec = "US-ASCII")

5

Java와 마찬가지로 CommonsIO 라이브러리를 사용하십시오.

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

또한 많은 답변이 Charset을 잊어 버렸습니다. 항상 명시 적으로 제공하는 것이 좋습니다. 그렇지 않으면 하루가 걸릴 것입니다.


4

파일을 열고 읽는 Ruby 구문을 에뮬레이션하고 의미를 전달하려면이 암시 적 클래스 (Scala 2.10 이상)를 고려하십시오.

import java.io.File

def open(filename: String) = new File(filename)

implicit class RichFile(val file: File) extends AnyVal {
  def read = io.Source.fromFile(file).getLines.mkString("\n")
}

이런 식으로,

open("file.txt").read

3

scala.io.Source를 언급 한 소수의 사람들 은 연결 누출로 인해 피하는 것이 가장 좋습니다.

아마도 새 인큐베이터 프로젝트 (예 : scala-io)가 병합 될 때까지 scalax와 commons-io와 같은 순수 Java 라이브러리가 최상의 옵션입니다.


3

scala io의 경로를 사용하여 파일을 읽고 처리 할 수도 있습니다.

import scalax.file.Path

이제 다음을 사용하여 파일 경로를 얻을 수 있습니다.

val filePath = Path("path_of_file_to_b_read", '/')
val lines = file.lines(includeTerminator = true)

종료자를 포함 할 수도 있지만 기본적으로 false로 설정되어 있습니다.



3

모든 한 줄을 구문 분석 한 다음 다시 연결할 필요가 없습니다 ...

Source.fromFile(path)(Codec.UTF8).mkString

나는 이것을 사용하는 것을 선호한다 :

import scala.io.{BufferedSource, Codec, Source}
import scala.util.Try

def readFileUtf8(path: String): Try[String] = Try {
  val source: BufferedSource = Source.fromFile(path)(Codec.UTF8)
  val content = source.mkString
  source.close()
  content
}

스트림을 닫아야합니다. 오류가 발생하면val content = source.mkString
Andrzej Jozwik

일에 대한 Codec. sbt testIntellij의 테스트 명령이 모든 테스트를 통과하는 동안 설정할 수 없기 때문에 테스트에 실패했습니다 . 그리고 당신은 사용할 수 있습니다 def using에서
미하일 Ionkin

3

타사 종속성이 마음에 들지 않으면 내 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")

한 줄에 대한 도우미와 바이트를 읽어 , 덩어리를 읽고 , 라인을 읽고 , 많은 다른 유용한 / 일반적인 작업


2

명백한 질문은 "전체 파일에서 왜 읽고 싶습니까?"입니다. 파일이 매우 커지면 확장 가능한 솔루션이 아닙니다. 는 scala.io.Source당신이를 다시 제공 Iterator[String]으로부터 getLines매우 유용하고 간결 방법.

그것은 변환하는 기본 자바 IO 유틸리티를 사용하여 암시 적 변환을 마련하는 작업의 많은 부분이 아니다 File하는 Reader또는를 InputStreamA를 String. 확장 성의 부족은 표준 API에 이것을 추가하지 않는 것이 옳다는 것을 의미한다고 생각합니다.


12
진심이야? 메모리에 실제로 문제가있는 정기적으로 얼마나 많은 파일을 읽습니까? 내가 다룬 대부분의 프로그램에서 대부분의 파일은 메모리에 들어가기에 충분히 작습니다. 솔직히 빅 데이터 파일은 예외이며, 읽거나 쓰려고 할 때이를 인식하고 프로그램해야합니다.
Christopher

8
oxbow_lakes, 동의하지 않습니다. 나중에 크기가 커지지 않는 작은 파일과 관련된 상황이 많이 있습니다.
브렌든 오코너

4
나는 그것들이 예외라는 것에 동의하지만, 이것이 메모리 전체 파일 읽기가 JDK 또는 Scala SDK에없는 이유라고 생각합니다. 스스로 작성하는 3 줄 유틸리티 방법입니다. 극복
oxbow_lakes

1

Java BufferedReader 사용 ervery 행 읽기와 같이 모든 행을 인쇄하고 인쇄하십시오.

scala.io.Source.fromFile("test.txt" ).foreach{  print  }

동등한:

scala.io.Source.fromFile("test.txt" ).foreach( x => print(x))

0
import scala.io.source
object ReadLine{
def main(args:Array[String]){
if (args.length>0){
for (line <- Source.fromLine(args(0)).getLine())
println(line)
}
}

인수로 파일 경로를 지정할 수 있으며 모든 줄을 반환합니다.


3
이것은 다른 답변이 제공하지 않는 것을 제공합니까?
jwvh

다른 답변을 보지 못했습니다 ... 그냥 내가 여기에 게시 할 수 있다고 생각했습니다 ...
아마도 그것은

1
정말로 읽어야합니다. 대부분은 유익한 정보입니다. 8 세인 사람들도 관련 정보를 가지고 있습니다.
jwvh
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.