Je lis un fichier csv. Pour ce faire, j'utilise Akka Streams afin de pouvoir créer un graphique des actions à effectuer sur chaque ligne. J'ai l'exemple de jouet suivant en cours d'exécution.
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem("MyAkkaSystem")
implicit val materializer = ActorMaterializer()
val source = akka.stream.scaladsl.Source.fromIterator(Source.fromFile("a.csv").getLines)
val sink = Sink.foreach(println)
source.runWith(sink)
}
Les deux types Source
ne sont pas faciles avec moi. Est-ce idiomatique ou existe-t-il une meilleure façon de l'écrire?
En fait, akka-streams
fournit une fonction permettant de lire directement à partir d'un fichier.
FileIO.fromPath(Paths.get("a.csv"))
.via(Framing.delimiter(ByteString("\n"), 256, true).map(_.utf8String))
.runForeach(println)
Ici, la méthode runForeach
consiste à imprimer les lignes. Si vous avez une Sink
propre à traiter ces lignes, utilisez-la à la place de cette fonction. Par exemple, si vous souhaitez fractionner les lignes par '
et en imprimer le nombre total de mots:
val sink: Sink[String] = Sink.foreach(x => println(x.split(",").size))
FileIO.fromPath(Paths.get("a.csv"))
.via(Framing.delimiter(ByteString("\n"), 256, true).map(_.utf8String))
.to(sink)
.run()
La façon idiomatique de lire un fichier CSV avec Akka Streams consiste à utiliser le connecteur Alpakka CSV . L'exemple suivant lit un fichier CSV, le convertit en une mappe de noms de colonnes (supposée être la première ligne du fichier) et les valeurs ByteString
, transforme les valeurs ByteString
en valeurs String
et affiche chaque ligne:
FileIO.fromPath(Paths.get("a.csv"))
.via(CsvParsing.lineScanner())
.via(CsvToMap.toMap())
.map(_.mapValues(_.utf8String))
.runForeach(println)
Oui, ça va parce que ce sont différents Source
name__s. Mais si vous n’aimez pas scala.io.Source
, vous pouvez lire le fichier vous-même (ce que nous devons parfois faire, par exemple, le fichier source csv
est compressé), puis l’analyser à l’aide de InputStream
comme ceci
StreamConverters.fromInputStream(() => input)
.via(Framing.delimiter(ByteString("\n"), 4096))
.map(_.utf8String)
.collect { line =>
line
}
Cela dit, envisagez d'utiliser Apache Commons CSV
avec akka-stream. Vous pouvez finir par écrire moins de code :)
Essaye ça:
package ru.io
import Java.nio.file.Paths
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl._
import akka.util.ByteString
import scala.concurrent.Await
import scala.concurrent.duration._
object ReadStreamApp extends App {
implicit val actorSystem = ActorSystem()
import actorSystem.dispatcher
implicit val flowMaterializer = ActorMaterializer()
// читать строки из файла журнала
val logFile = Paths.get("src/main/resources/a.csv")
val source = FileIO.fromPath(logFile)
// анализировать фрагменты байтов в строки
val flow = Framing
.delimiter(ByteString(System.lineSeparator()), maximumFrameLength = 512, allowTruncation = true)
.map(_.utf8String)
val sink = Sink.foreach(println)
source
.via(flow)
.runWith(sink)
.andThen {
case _ =>
actorSystem.terminate()
Await.ready(actorSystem.whenTerminated, 1 minute)
}
}