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