2009-08-16 4 views
277

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

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

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

или я должен использовать один из Java's god-awful idioms, лучшие из которых (без использования внешней библиотеки) кажется:

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

+19

Этот комментарий не соответствует точке дизайна языка. Любой язык, который имеет доступную простую библиотечную функцию для точно такой операции, которую вы хотите выполнить, как его синтаксис вызова функции. Учитывая бесконечную и 100% сохраненную в памяти библиотеку, все программы будут реализованы с помощью одного вызова функции. Язык программирования хорош, когда ему требуется меньше предварительно готовых компонентов, чтобы уже существовать, чтобы достичь конкретный результат. –

ответ

378
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" 
+0

А, безусловно, лучше, чем reduceLeft (_ + _). –

+41

Я опоздал на вечеринку, но мне бы не хотелось, чтобы люди не знали, что они могут делать «io.File («/etc/passwd ") .slurp" в trunk. – extempore

+6

Я бы не хотел, чтобы Scala 2.8 имел метод под названием «slurp», но, похоже, я застрял в любом случае. –

34
// 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

1

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

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

+11

Серьезно? Сколько файлов вы действительно читаете на регулярной основе, у которых есть реальные проблемы, связанные с памятью? Подавляющее большинство файлов в подавляющем большинстве программ, которые я когда-либо рассматривал, достаточно малы, чтобы вписаться в память. Честно говоря, большие файлы данных являются исключением, и вы должны понимать это и программировать соответственно, если вы собираетесь их читать и писать. – Christopher

+8

oxbow_lakes, я не согласен. Существует много ситуаций, связанных с небольшими файлами, размер которых в будущем не будет расти. –

+4

Я согласен с тем, что они являются исключением, но я думаю, что поэтому чтение-цельный файл-в-память не находится ни в JDK, ни в Scala SDK. Это 3-строчный метод утилиты для вас: напишите: –

50

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

import scala.io.Source._ 

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

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

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

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

+0

OK. Есть история моего негативного впечатления от Источника: я когда-то был в другой ситуации, чем сейчас, где у меня был очень большой файл, который не поместился бы в память.Использование источника вызвало сбой программы; оказалось, что он пытался сразу прочитать все это. –

+6

Источник не должен читать весь файл в памяти. Если вы используете toList после getLines или какой-либо другой метод, который будет создавать коллекцию, тогда вы получите все в память. Теперь Source - это * hack *, предназначенный для выполнения работы, а не для тщательно продуманной библиотеки. Он будет улучшен в Scala 2.8, но определенно возможно, что сообщество Scala станет активным в определении хорошего API ввода-вывода. –

6

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

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

+1

+1 для упоминания scalax –

24

(EDIT: Это не работает в Скале 2.9, может быть, не 2,8 либо)

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

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

"' slurp' "? Неужели мы действительно бросили очевидное, интуитивное имя? Проблема с 'slurp' заключается в том, что это может иметь смысл после того, как кто-то с английским как первый язык, по крайней мере, но вы никогда не подумаете об этом для начала! –

+4

Просто наткнулся на этот вопрос/ответ. 'File' больше не находится в 2.8.0, не так ли? – huynhjl

+3

Вы все еще можете его проскользнуть из файла scala.tools.nsc.io.File, хотя я предполагаю, что местоположение может измениться в будущем, поэтому используйте его на свой страх и риск. ;-) О, и позвольте мне перезвонить, чтобы сказать, насколько я ненавижу «slurp» как имя. – Steve

2

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

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

5

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

Ниже следует сохранить его характер-для- характер, и не делает чрезмерной конкатенации (проблемы производительности):

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

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

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

эквивалент:

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

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

import scalax.file.Path 

Теперь вы можете получить путь к файлу с помощью этого: -

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

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

2

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

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

Примечание Source.scala. Для дальнейшего обсуждения см. Scala fast text file read and upload to memory.

4

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

FileUtils.readFileToString(file, StandardCharsets.UTF_8) 

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

2

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

Еще одно: 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. по умолчанию, если вы не укажете):

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

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

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 
} 
10
import java.nio.charset.StandardCharsets._ 
import java.nio.file.{Files, Paths} 

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

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

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

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

+0

Что это предложение, чтобы другой ответ не ответил? – jwvh

+0

Не видели других ответов ... просто подумал, что могу внести здесь свой вклад ... надеюсь, это никого не повредит :) – Apurw

+0

Вы действительно должны их прочитать. Большинство из них весьма информативны. Даже те, кому 8 лет, имеют соответствующую информацию. – jwvh

Смежные вопросы