web-dev-qa-db-fra.com

Lire le fichier entier dans Scala?

Qu'est-ce qu'un moyen simple et canonique de lire un fichier entier en mémoire dans Scala? (Idéalement, avec un contrôle sur l'encodage des caractères.)

Le mieux que je puisse trouver est:

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

ou suis-je censé utiliser l'un des idiomes terribles de Java , dont le meilleur (sans utiliser une bibliothèque externe) semble être:

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

À la lecture des discussions sur les listes de diffusion, il n’est pas clair pour moi que scala.io.Source est même supposé être la bibliothèque canonique d’E/S. Je ne comprends pas quel est son but, exactement.

... Je voudrais quelque chose de très simple et facile à retenir. Par exemple, dans ces langues, il est très difficile d'oublier l'idiome ...

Ruby    open("file.txt").read
Ruby    File.read("file.txt")
Python  open("file.txt").read()
301
Brendan OConnor
val lines = scala.io.Source.fromFile("file.txt").mkString

En passant, "scala." n'est pas vraiment nécessaire, car il est toujours dans la portée, et vous pouvez, bien sûr, importer le contenu de io, totalement ou partiellement, et éviter d'avoir à ajouter "io". aussi.

Ce qui précède laisse le fichier ouvert, cependant. Pour éviter les problèmes, vous devriez le fermer comme ceci:

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

Un autre problème avec le code ci-dessus est qu’il est horriblement lent en raison de sa nature de mise en œuvre. Pour les fichiers plus volumineux, il faut utiliser:

source.getLines mkString "\n"
416
Daniel C. Sobral

Juste pour développer la solution de Daniel, vous pouvez raccourcir considérablement les choses en insérant l'importation suivante dans tout fichier nécessitant une manipulation de fichier:

import scala.io.Source._

Avec cela, vous pouvez maintenant faire:

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

Je me méfierais de la lecture d'un fichier entier dans un seul String. C'est une très mauvaise habitude, une habitude qui vous mordra plus tôt et plus fort que vous ne le pensez. La méthode getLines renvoie une valeur de type Iterator[String]. C'est en fait un curseur paresseux dans le fichier, vous permettant d'examiner uniquement les données dont vous avez besoin sans risquer de saturer la mémoire.

Oh, et pour répondre à votre question implicite à propos de Source: oui, c’est la bibliothèque canonique d’E/S. La plupart du code finit par utiliser Java.io en raison de son interface de niveau inférieur et d'une meilleure compatibilité avec les frameworks existants, mais tout code ayant le choix devrait utiliser Source, en particulier pour la manipulation de fichier simple.

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

(EDIT: Cela ne fonctionne pas dans scala 2.9 et peut-être pas 2.8 non plus)

Utiliser le coffre:

scala> io.File("/etc/passwd").Slurp
res0: String = 
##
# User Database
# 
... etc
26
psp
import Java.nio.charset.StandardCharsets._
import Java.nio.file.{Files, Paths}

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

Contrôle de l'encodage des caractères et pas de ressources à nettoyer. Également, éventuellement optimisé (par exemple Files.readAllBytes allouant un tableau d'octets adapté à la taille du fichier).

17
Paul Draper

On m'a dit que Source.fromFile est problématique. Personnellement, j’ai eu des problèmes pour ouvrir de gros fichiers avec Source.fromFile et j’ai dû faire appel à Java InputStreams.

Une autre solution intéressante consiste à utiliser scalax. Voici un exemple de code bien commenté qui ouvre un fichier journal à l'aide de ManagedResource pour ouvrir un fichier avec les aides scalax: http://pastie.org/pastes/420714

7
Ikai Lan

Utiliser getLines () sur scala.io.Source ignore les caractères utilisés pour les fins de ligne (\ n,\r,\r\n, etc.)

Les éléments suivants doivent le conserver caractère par caractère et ne pas effectuer de concaténation de chaînes excessive (problèmes de performances):

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
Muyyatin

Tout comme en Java, en utilisant la bibliothèque CommonsIO:

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

En outre, de nombreuses réponses ici oublient Charset. Il est préférable de toujours le fournir explicitement, sinon cela se produira un jour.

5
Dzmitry Lazerka

Pour émuler la syntaxe Ruby (et transmettre la sémantique) de l'ouverture et de la lecture d'un fichier, considérons cette classe implicite (Scala 2.10 et supérieur),

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 cette façon,

open("file.txt").read
4
elm

Un de plus: https://github.com/pathikrit/better-files#streams-and-codecs

Différentes manières de supprimer un fichier sans charger le contenu dans la mémoire:

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 

Vous pouvez également fournir votre propre codec pour tout ce qui effectue une lecture/écriture (il suppose scala.io.Codec.default si vous n'en fournissez pas):

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

vous pouvez également utiliser Path from scala io pour lire et traiter les fichiers.

import scalax.file.Path

Maintenant, vous pouvez obtenir le chemin du fichier en utilisant ceci: -

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

Vous pouvez également inclure des terminateurs, mais par défaut, il est défini sur false.

3
Atiq

comme quelques personnes l'ont mentionné scala.io.Source est préférable d'éviter en raison de fuites de connexion.

Les polices scalax et pure Java comme commons-io sont probablement les meilleures options jusqu'à la fusion du nouveau projet d'incubateur (c'est-à-dire scala-io).

3
poko

Pour accélérer la lecture et le téléchargement d’un fichier (volumineux), envisagez d’augmenter la taille de bufferSize (Source.DefaultBufSize réglé sur 2048), par exemple, comme suit:

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

Remarque Source.scala . Pour plus de détails, voir fichier texte rapide Scala lu et téléchargé dans la mémoire .

3
elm

La question évidente étant "pourquoi voulez-vous lire dans tout le fichier?" Ce n'est évidemment pas une solution évolutive si vos fichiers deviennent très volumineux. Le scala.io.Source vous renvoie un Iterator[String] de la méthode getLines, ce qui est très utile et concis.

Trouver une conversion implicite à l'aide des utilitaires sous-jacents Java IO sous-jacent pour convertir un File, un Reader ou un InputStream à un String. Je pense que le manque d'évolutivité signifie qu'ils ont raison de ne pas l'ajouter à l'API standard.

2
oxbow_lakes
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 arguments, vous pouvez donner le chemin du fichier et il retournera toutes les lignes

1
Apurw

Si une dépendance vis-à-vis de tiers ne vous gêne pas, vous devriez envisager d'utiliser my bibliothèque OS-Lib . Cela rend la lecture/écriture de fichiers et le travail avec le système de fichiers très pratique:

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

avec des aides d'une ligne pour lecture d'octets , lecture de morceaux , lignes de lecture , et de nombreuses autres opérations utiles/courantes

1
Li Haoyi

affiche toutes les lignes, par exemple Java BufferedReader read ervery line, et l’affiche:

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

équivalent:

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

Vous n'avez pas besoin d'analyser chaque ligne et de les concaténer à nouveau ...

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

Je préfère utiliser ceci:

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
}
1
comonad