Прочитать весь файл в Scala?

Какой простой и канонический способ прочитать весь файл в памяти в Scala? (В идеале, с контролем кодировки символов.)

Лучшее, что я могу придумать, это:

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 даже должен быть канонической библиотекой ввода-вывода. Я не понимаю, какова его цель.

... Мне хотелось бы что-то мертвое - просто и легко запомнить. Например, на этих языках очень сложно забыть идиому...

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

Ответ 1

val lines = scala.io.Source.fromFile("file.txt").mkString
Кстати, "scala." на самом деле не обязательно, как и всегда в области видимости, и вы можете, конечно, импортировать содержимое, полностью или частично, и избегать добавления "io". тоже.

Вышеприведенный файл оставляет открытым файл. Чтобы избежать проблем, вы должны закрыть его следующим образом:

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

Другая проблема с вышеприведенным кодом заключается в том, что он ужасно медленный из-за его природы реализации. Для больших файлов следует использовать:

source.getLines mkString "\n"

Ответ 2

Чтобы расширить решение Daniel, вы можете значительно сократить его, вставив следующий импорт в любой файл, который требует манипуляции с файлами:

import scala.io.Source._

С этим вы можете теперь:

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

Я бы с осторожностью прочитал весь файл в одном String. Это очень плохая привычка, которая укусит вас раньше и сложнее, чем вы думаете. Метод getLines возвращает значение типа Iterator[String]. Это эффективный ленивый курсор в файл, позволяющий вам просматривать только нужные вам данные, не рискуя переутомлением памяти.

О, и ответить на ваш предполагаемый вопрос о Source: да, это каноническая библиотека ввода-вывода. В большинстве случаев код заканчивается использованием java.io из-за его интерфейса более низкого уровня и лучшей совместимости с существующими фреймворками, но любой код, который имеет выбор, должен использовать Source, особенно для простой манипуляции файлами.

Ответ 3

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

Ответ 4

(EDIT: Это не работает в scala 2.9 и, возможно, не 2.8)

Использовать соединительную линию:

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

Ответ 5

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

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

Управление кодировкой символов и отсутствие ресурсов для очистки. Также возможно оптимизировано (например, Files.readAllBytes распределяющее массив байтов, соответствующий размеру файла).

Ответ 6

Мне сказали, что Source.fromFile проблематичен. Лично у меня возникли проблемы с открытием больших файлов с помощью Source.fromFile и пришлось прибегать к Java InputStreams.

Еще одно интересное решение - использование scalax. Вот пример некоторого хорошо прокомментированного кода, который открывает файл журнала с помощью ManagedResource для открытия файла с помощью помощников scalax: http://pastie.org/pastes/420714

Ответ 7

Использование getLines() в scala.io.Source отбрасывает, какие символы использовались для терминаторов строк (\n,\r,\r\n и т.д.)

Следующее должно сохранить символ character-for-character и не выполнять чрезмерную конкатенацию строк (проблемы с производительностью):

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

Ответ 8

Как и в Java, с помощью библиотеки CommonsIO:

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

Кроме того, многие ответы здесь забывают о Charset. Лучше всегда предоставлять его явно, или он ударит один день.

Ответ 9

Для эмуляции синтаксиса 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

Ответ 10

Еще одно: 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")

Ответ 11

поскольку несколько человек упомянули scala.io.Source лучше избегать из-за утечек соединения.

Вероятно, scalax и чистые java-библиотеки, такие как commons-io, являются лучшими опциями до тех пор, пока новый проект инкубатора (т.е. scala -io) не будет слит.

Ответ 12

вы также можете использовать Path из scala io для чтения и обработки файлов.

import scalax.file.Path

Теперь вы можете получить путь к файлу, используя это: -

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

Вы также можете включить терминаторы, но по умолчанию установлено значение false.

Ответ 13

Для более быстрого общего чтения/загрузки (большого) файла рассмотрите возможность увеличения размера bufferSize (Source.DefaultBufSize на 2048), например, следующим образом:

val file = new java.io.File("myFilename")
io.Source.fromFile(file, bufferSize = Source.DefaultBufSize * 2)

Примечание Source.scala. Для дальнейшего обсуждения см. Scala быстрый текстовый файл для чтения и загрузки в память.

Ответ 14

Очевидный вопрос: "Почему вы хотите читать во всем файле?" Это, очевидно, не масштабируемое решение, если ваши файлы становятся очень большими. scala.io.Source возвращает Iterator[String] метод getLines, который очень полезен и краток.

Не так много работы, чтобы придумать неявное преобразование, используя базовые утилиты java IO для преобразования File, a Reader или InputStream в String. Я считаю, что отсутствие масштабируемости означает, что они не соответствуют этому стандарту API.

Ответ 15

напечатайте каждую строку, например, используйте Java BufferedReader для чтения строки ervery и напечатайте ее:

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

эквивалентны:

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

Ответ 16

Вам не нужно анализировать каждую строку, а затем снова конкатенировать их...

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
}

Ответ 17

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

в аргументах вы можете указать путь к файлу, и он вернет все строки