Читать весь файл в 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()

19 ответов

Решение
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"

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

import scala.io.Source._

С этим теперь вы можете сделать:

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

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

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

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

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

Контроль над кодировкой символов, и нет ресурсов для очистки. Кроме того, оптимизирован, так как Files.readAllBytes выделяет байтовый массив, который соответствует размеру файла.

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

(РЕДАКТИРОВАТЬ: Это не работает в Scala 2.9 и, возможно, не 2.8)

Используйте ствол:

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

Если вы не возражаете против сторонней зависимости, вам следует рассмотреть возможность использования моей библиотеки 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")

с однострочными помощниками для чтения байтов, чтения фрагментов, строк и многих других полезных / распространенных операций

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

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

Использование getLines() в scala.io.Source отменяет, какие символы использовались для разделителей строк (\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)
}

Как и в Java, используя библиотеку CommonsIO:

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

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

Еще один: https://github.com/pathikrit/better-files

Различные способы сохранить файл без загрузки содержимого в память:

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

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

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
}

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

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

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

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

import scalax.file.Path

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

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

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

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

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

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

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

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

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

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

эквивалент:

scala.io.Source.fromFile("test.txt" ).foreach( x => print(x))
import scala.io.source
object ReadLine{
def main(args:Array[String]){
if (args.length>0){
for (line <- Source.fromLine(args(0)).getLine())
println(line)
}
}

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

Вы можете использовать

      Source.fromFile(fileName).getLines().mkString

однако следует отметить, что getLines() удаляет все символы новой строки. Если вы хотите сохранить форматирование, вы должны использовать

      Source.fromFile(fileName).iter.mkString
Другие вопросы по тегам