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()
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"
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.
// for file with utf-8 encoding
val lines = scala.io.Source.fromFile("file.txt", "utf-8").getLines.mkString
(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
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).
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
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)
}
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.
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
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")
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.
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).
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 .
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.
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
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
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))
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
}