web-dev-qa-db-fra.com

Lecture de fichiers CSV avec Akka Streams

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?

9
Mika'il

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()
13
fcat

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)
10
Jeffrey Chung

Oui, ça va parce que ce sont différents Sourcename__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 csvest compressé), puis l’analyser à l’aide de InputStreamcomme 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 :)

2
expert

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)
    }
}
1
Dmitry Kaltovich