Прочитать весь файл в 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)
}
}
в аргументах вы можете указать путь к файлу, и он вернет все строки