¿Leer todo el archivo en Scala?


312

¿Cuál es una manera simple y canónica de leer un archivo completo en la memoria en Scala? (Idealmente, con control sobre la codificación de caracteres).

Lo mejor que se me ocurre es:

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

o se supone que debo usar uno de los espantosos modismos de Java , el mejor de los cuales (sin usar una biblioteca externa) parece ser:

import java.util.Scanner
import java.io.File
new Scanner(new File("file.txt")).useDelimiter("\\Z").next()

Al leer las discusiones de la lista de correo, no me queda claro que scala.io.Source incluso sea la biblioteca de E / S canónica. No entiendo cuál es su propósito, exactamente.

... Me gustaría algo muy simple y fácil de recordar. Por ejemplo, en estos idiomas es muy difícil olvidar el idioma ...

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

12
Java no es tan malo si conoces las herramientas adecuadas. import org.apache.commons.io.FileUtils; FileUtils.readFileToString (nuevo archivo ("file.txt", "UTF-8")
smartnut007

25
Este comentario pierde el punto del diseño del lenguaje. Por lo tanto, cualquier lenguaje que tenga disponible una función de biblioteca simple para exactamente la operación que desea realizar es tan buena como su sintaxis de invocación de función. Dada una biblioteca infinita y 100% memorizada, todos los programas se implementarían con una sola llamada de función. Un lenguaje de programación es bueno cuando necesita que existan menos componentes prefabricados para lograr un resultado específico.
Chris Mountford

Respuestas:


429
val lines = scala.io.Source.fromFile("file.txt").mkString

Por cierto, " scala." no es realmente necesario, ya que de todos modos siempre está dentro del alcance y, por supuesto, puede importar el contenido de io, total o parcialmente, y evitar tener que anteponer "io". también.

Sin embargo, lo anterior deja el archivo abierto. Para evitar problemas, debe cerrarlo así:

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

Otro problema con el código anterior es que es muy lento debido a su naturaleza de implementación. Para archivos más grandes uno debe usar:

source.getLines mkString "\n"

48
Llego demasiado tarde a la fiesta, pero odiaría que la gente no supiera que pueden hacer "io.File (" / etc / passwd "). Sorber" en el baúl.
psp

28
@extempore Si realmente crees que soy ingrato, lo siento mucho. Le agradezco profundamente su apoyo al lenguaje Scala y cada vez que ha investigado personalmente un problema que mencioné, sugirió una solución a un problema que tenía o me explicó algo. Aprovecharé la oportunidad para agradecerte por convertir scala.io en algo decente y digno. Seré más vocal en mi agradecimiento de ahora en adelante, pero aún odio el nombre, lo siento.
Daniel C. Sobral

49
"sorber" ha sido el nombre para leer un archivo completo a la vez en Perl durante muchos años. Perl tiene una tradición de nombres más visceral e informal que la familia de lenguajes C, que algunos pueden encontrar desagradable, pero en este caso creo que encaja: es una palabra fea para una práctica fea. Cuando sorbas (), sabes que estás haciendo algo malo porque solo tienes que escribir eso.
Marcus Downing el

15
File.read () sería un nombre más agradable y, además, coherente con Ruby y Python.
Brendan OConnor

26
@extempore: no puedes evitar que la gente se disguste. Así es como es. No debería molestarte que a algunas personas no les guste cada elección que hayas hecho. Así es la vida, no puedes complacer a todos :)
Alex Baranosky

58

Solo para ampliar la solución de Daniel, puede acortar enormemente las cosas insertando la siguiente importación en cualquier archivo que requiera manipulación de archivos:

import scala.io.Source._

Con esto, ahora puedes hacer:

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

Sería cauteloso de leer un archivo completo en uno solo String. Es un mal hábito, uno que te morderá antes y con más fuerza de lo que piensas. El getLinesmétodo devuelve un valor de tipo Iterator[String]. Es efectivamente un cursor lento en el archivo, lo que le permite examinar solo los datos que necesita sin arriesgar el exceso de memoria.

Ah, y para responder a su pregunta implícita sobre Source: sí, es la biblioteca de E / S canónica. La mayoría del código termina usando java.iodebido a su interfaz de nivel inferior y una mejor compatibilidad con los marcos existentes, pero cualquier código que tenga una opción debe usarse Source, particularmente para la simple manipulación de archivos.


OKAY. Hay una historia para mi impresión negativa de Source: una vez estuve en una situación diferente a la actual, donde tenía un archivo muy grande que no cabía en la memoria. El uso de Source provocó el bloqueo del programa; Resultó que estaba tratando de leer todo de una vez.
Brendan OConnor

77
Se supone que la fuente no lee todo el archivo en la memoria. Si usa toList después de getLines, o algún otro método que produzca una colección, entonces lo guarda todo en la memoria. Ahora, Source es un truco , destinado a hacer el trabajo, no una biblioteca cuidadosamente pensada. Se mejorará en Scala 2.8, pero definitivamente hay una oportunidad para que la comunidad Scala se active en la definición de una buena API de E / S.
Daniel C. Sobral

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

66
Agregar "getLines" a la respuesta original eliminará todas las nuevas líneas. Debe ser "Source.fromFile (" file.txt "," utf-8 "). MkString".
Joe23

99
Vea también mi comentario en la respuesta de Daniel C. Sobral: este uso no cerrará la instancia de Source, por lo que Scala puede retener un bloqueo en el archivo.
DJ

26

(EDITAR: Esto no funciona en scala 2.9 y quizás tampoco 2.8)

Usar baúl:

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

14
" slurp"? ¿Realmente hemos abandonado el nombre obvio e intuitivo? El problema slurpes que puede tener sentido después de los hechos, para alguien con el inglés como primer idioma, al menos, ¡pero para empezar, nunca pensarías en eso!
Daniel C. Sobral

55
Acabo de tropezar con esta pregunta / respuesta. Fileya no está en 2.8.0, ¿no?
huynhjl

44
sorber suena genial. :) No lo esperaría, pero tampoco esperaba que la salida a la pantalla se llamara 'imprimir'. slurp¡es fantastico! :) ¿Fue fantastico? No lo encuentro ; (
usuario desconocido el

55
en scala-2.10.0 el nombre del paquete es scala.reflect.io.File Y una pregunta sobre este "Archivo". extempore, ¿por qué este archivo está marcado como "experimental"? ¿Es seguro? ¿Libera un bloqueo al sistema de archivos?
VasiliNovikov

44
slurp tiene una larga historia para este propósito originando, creo, de perl
Chris Mountford

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

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

Control sobre la codificación de caracteres y sin recursos para limpiar. Además, posiblemente optimizado (por ejemplo, Files.readAllBytesasignando una matriz de bytes adecuada al tamaño del archivo).


7

Me han dicho que Source.fromFile es problemático. Personalmente, tuve problemas para abrir archivos grandes con Source.fromFile y tuve que recurrir a Java InputStreams.

Otra solución interesante es usar scalax. Aquí hay un ejemplo de un código bien comentado que abre un archivo de registro usando ManagedResource para abrir un archivo con ayudantes de scalax: http://pastie.org/pastes/420714


6

El uso de getLines () en scala.io.Source descarta qué caracteres se usaron para los terminadores de línea (\ n, \ r, \ r \ n, etc.)

Lo siguiente debería preservarlo carácter por carácter, y no hace una concatenación excesiva de cadenas (problemas de rendimiento):

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

6

Uno más: https://github.com/pathikrit/better-files#streams-and-codecs

Varias formas de sorber un archivo sin cargar el contenido en la memoria:

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 

También puede suministrar su propio códec para cualquier cosa que haga una lectura / escritura (se supone scala.io.Codec.default si no proporciona uno):

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

5

Al igual que en Java, usando la biblioteca CommonsIO:

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

Además, muchas respuestas aquí olvidan Charset. Es mejor proporcionarlo siempre explícitamente, o llegará algún día.


4

Para emular la sintaxis de Ruby (y transmitir la semántica) de abrir y leer un archivo, considere esta clase implícita (Scala 2.10 y superior),

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

De este modo,

open("file.txt").read

3

como algunas personas mencionaron scala.io.Source es mejor evitarlo debido a fugas de conexión.

Probablemente, scalax y las bibliotecas Java puras como commons-io son las mejores opciones hasta que el nuevo proyecto de incubadora (es decir, scala-io) se fusione.


3

También puede usar Path from Scala io para leer y procesar archivos.

import scalax.file.Path

Ahora puede obtener la ruta del archivo con esto: -

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

También puede incluir terminadores, pero de forma predeterminada está establecido en falso.


3

Para una lectura / carga general más rápida de un archivo (grande), considere aumentar el tamaño de bufferSize( Source.DefaultBufSizeestablecido en 2048), por ejemplo, de la siguiente manera,

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

Nota Source.scala . Para una discusión más detallada, vea el archivo de texto rápido Scala leído y cargado en la memoria .


3

No es necesario analizar cada línea y luego volver a concatenarlas ...

Source.fromFile(path)(Codec.UTF8).mkString

Prefiero usar esto:

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
}

Debería cerrar la transmisión, si se produce un error enval content = source.mkString
Andrzej Jozwik

+1 para Codec. Tengo prueba deficiente en sbt testporque no se puede establecer que, mientras comando de prueba de IntelliJ pasar todas las pruebas. Y se puede usar def usingdesde este
Mikhail Ionkin

3

Si no le importa una dependencia de terceros, debería considerar usar mi biblioteca OS-Lib . Esto hace que leer / escribir archivos y trabajar con el sistema de archivos sea muy conveniente:

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

con ayudantes de una línea para leer bytes , leer fragmentos , leer líneas y muchas otras operaciones útiles / comunes


2

La pregunta obvia es "¿por qué quieres leer todo el archivo?" Obviamente, esta no es una solución escalable si sus archivos se hacen muy grandes. El scala.io.Sourceda hacer una copia de un Iterator[String]Del getLinesmétodo, que es muy útil y concisa.

No es un gran trabajo lograr una conversión implícita utilizando las utilidades Java IO subyacentes para convertir a File, a Readero an en InputStreama String. Creo que la falta de escalabilidad significa que son correctos para no agregar esto a la API estándar.


12
¿Seriamente? ¿Cuántos archivos lees realmente de forma regular que tienen problemas reales de ajuste en la memoria? La gran mayoría de los archivos en la gran mayoría de los programas con los que he tratado son fácilmente lo suficientemente pequeños como para caber en la memoria. Francamente, los archivos de big data son la excepción, y debe darse cuenta de eso y programar en consecuencia si va a leerlos / escribirlos.
Christopher

8
oxbow_lakes, no estoy de acuerdo. Hay muchas situaciones que involucran archivos pequeños cuyo tamaño no crecerá en el futuro.
Brendan OConnor

44
Estoy de acuerdo en que son la excepción, pero creo que es por eso que un archivo completo de lectura en memoria no está en el JDK ni en el SDK de Scala. Es un método de utilidad de 3 líneas para que usted mismo escriba:
supérelo

1

imprima cada línea, como utilizar Java BufferedReader, lea cada línea e imprímala:

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

equivalente:

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

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

en argumentos, puede dar la ruta del archivo y devolverá todas las líneas


3
¿Qué ofrece esto que la otra respuesta no ofrece?
jwvh

No he visto otras respuestas ... solo pensé que podría contribuir aquí, así que publicado ... espero que eso no haga daño a nadie :)
Apurw

1
Realmente deberías leerlos. La mayoría son bastante informativos. Incluso los que tienen 8 años tienen información relevante.
jwvh
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.