software - Leer el archivo completo en Scala?
scala tutorial (17)
(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
¿Cuál es una forma 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 idiotas 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()
Después de leer las discusiones de la lista de correo, no tengo claro que scala.io.Source se supone que es la biblioteca de E / S canónica. No entiendo cuál es su propósito, exactamente.
... Me gustaría algo muerto, simple y fácil de recordar. Por ejemplo, en estos idiomas es muy difícil olvidar el lenguaje ...
Ruby open("file.txt").read
Ruby File.read("file.txt")
Python open("file.txt").read()
Al igual que en Java, utilizando la biblioteca CommonsIO:
FileUtils.readFileToString(file, StandardCharsets.UTF_8)
Además, muchas respuestas aquí se olvidan de Charset. Siempre es mejor proporcionarlo explícitamente, o llegará un día.
Como mencionaron algunas personas, es mejor evitar scala.io.Source debido a las fugas de conexión.
Probablemente scalax y las librerías de java puro como commons-io son las mejores opciones hasta que el nuevo proyecto de incubadora (es decir, scala-io) se fusione.
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 debe preservarlo carácter por carácter, y no hace una concatenación de cadena excesiva (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)
}
La pregunta obvia es "¿por qué quieres leer todo el archivo?" Obviamente, esto no es una solución escalable si sus archivos se vuelven muy grandes. El scala.io.Source
le devuelve un Iterator[String]
del método getLines
, que es muy útil y conciso.
No es un gran trabajo crear una conversión implícita utilizando las utilidades subyacentes de IO de Java para convertir un File
, un Reader
o un InputStream
en una String
. Creo que la falta de escalabilidad significa que es correcto no agregar esto a la API estándar.
Me han dicho que Source.fromFile es problemático. Personalmente, he tenido problemas al abrir archivos grandes con Source.fromFile y he tenido 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
No es necesario analizar cada línea y luego concatenarlas de nuevo ...
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
}
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
Para una lectura / carga general más rápida de un archivo (grande), considere aumentar el tamaño de bufferSize
( Source.DefaultBufSize
establecido en 2048
), por ejemplo, como sigue:
val file = new java.io.File("myFilename")
io.Source.fromFile(file, bufferSize = Source.DefaultBufSize * 2)
Note Source.scala . Para una discusión más detallada, vea el archivo de texto rápido Scala leído y cargado en la memoria .
Solo para ampliar la solución de Daniel, puede acortar las cosas enormemente insertando la siguiente importación en cualquier archivo que requiera la manipulación del archivo:
import scala.io.Source._
Con esto, ahora puedes hacer:
val lines = fromFile("file.txt").getLines
Me preocuparía leer un archivo completo en una sola String
. Es un hábito muy malo que te morderá más pronto de lo que piensas. El método getLines
devuelve un valor de tipo Iterator[String]
. Es efectivamente un cursor perezoso en el archivo, lo que le permite examinar solo los datos que necesita sin correr el riesgo de exceso de memoria.
Ah, y para responder a tu pregunta implícita sobre Source
: sí, es la biblioteca de E / S canónica. La mayoría de los códigos terminan usando java.io
debido a su interfaz de nivel inferior y una mejor compatibilidad con los marcos existentes, pero cualquier código que tenga una opción debería usar Source
, particularmente para la manipulación simple de archivos.
También puede usar la ruta de acceso de Scala para leer y procesar archivos.
import scalax.file.Path
Ahora puedes obtener la ruta del archivo usando 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, se establece en falso.
Uno más: https://github.com/pathikrit/better-files#streams-and-codecs
Varias formas de absorber 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 (asume 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")
imprima cada línea, como usar Java BufferedReader read ervery line, e imprímala:
scala.io.Source.fromFile("test.txt" ).foreach{ print }
equivalente:
scala.io.Source.fromFile("test.txt" ).foreach( x => print(x))
// for file with utf-8 encoding
val lines = scala.io.Source.fromFile("file.txt", "utf-8").getLines.mkString
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 no hay recursos para limpiar. También, optimizado, ya que Files.readAllBytes
asigna una matriz de bytes que coincide con el tamaño del archivo.
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 puedes dar ruta de archivo y devolverá todas las líneas.
val lines = scala.io.Source.fromFile("file.txt").mkString
Por cierto, " scala.
" no es realmente necesario, ya que siempre está dentro del alcance, y puedes, por supuesto, importar el contenido de io, total o parcialmente, y evitar tener que añadir "io". también.
Lo anterior deja el archivo abierto, sin embargo. Para evitar problemas, debes 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 extremadamente lento debido a su naturaleza de implementación. Para archivos más grandes se debe usar:
source.getLines mkString "/n"